void FuncReturningFoo(SLType type, SLConstant val) { SLLine line = SLReturn.ReturnLine(val); SLCodeBlock block = new SLCodeBlock(new ICodeElement [] { line }); SLFunc func = new SLFunc(Visibility.Public, type, new SLIdentifier("simpleFunc"), null, block); string code = CodeWriter.WriteToString(func); Compiler.CompileStringUsing(null, XCodeCompiler.SwiftcCustom, code, null); }
static SLFunc MetadataFuncForType(PlatformName platform, TypeDefinition type, TypeType entityType) { var name = type.Name; var moduleName = type.Namespace; // typename based on C# type name, not remapped name var funcID = new SLIdentifier(FuncIDForTypeDefinition(type)); TypeAggregator.RemapModuleAndName(platform, ref moduleName, ref name, entityType); var value = new SLIdentifier($"{name}.self"); var returnLine = SLReturn.ReturnLine(value); var body = new SLCodeBlock(null); body.Add(returnLine); var func = new SLFunc(Visibility.Public, new SLSimpleType("Any.Type"), funcID, null, body); new SLComment(type.FullName, true).AttachBefore(func); var attr = AvailableAttributeForType(platform, type); if (attr != null) { attr.AttachBefore(func); } return(func); }
public IEnumerable <ICodeElement> MarshalFunctionCall(FunctionDeclaration func, string vtableName, string vtableElementName) { preMarshalCode.Clear(); postMarshalCode.Clear(); ICodeElement returnLine = null; var instanceEntity = typeMapper.GetEntityForTypeSpec(func.ParameterLists [0] [0].TypeSpec); bool instanceIsProtocol = instanceEntity.EntityType == EntityType.Protocol; // bool returnIsGeneric = func.ReturnTypeSpec != null && func.IsGenericType(func.ReturnTypeSpec); SLIdentifier returnIdent = null; if (func.ParameterLists.Count != 2) { throw new ArgumentException(String.Format("Method {0} is has {1} parameter lists - it should really have two (normal for an instance method).", func.ToFullyQualifiedName(true), func.ParameterLists.Count)); } var closureArgs = new List <SLBaseExpr> (); // marshal the regular arguments for (int i = 0; i < func.ParameterLists [1].Count; i++) { var parm = func.ParameterLists [1] [i]; var privateName = !String.IsNullOrEmpty(parm.PrivateName) ? parm.PrivateName : TypeSpecToSLType.ConjureIdentifier(null, i).Name; if (func.IsTypeSpecGeneric(parm)) { Tuple <int, int> depthIndex = func.GetGenericDepthAndIndex(parm.TypeSpec); closureArgs.Add(MarshalGenericTypeSpec(func, privateName, parm.TypeSpec as NamedTypeSpec, depthIndex.Item1, depthIndex.Item2)); } else { closureArgs.Add(MarshalTypeSpec(func, privateName, parm.TypeSpec)); } } string callName = String.Format("{0}.{1}", vtableName, vtableElementName); var callInvocation = new SLPostBang(new SLIdentifier(callName), false); if (instanceIsProtocol) { string protoName = MarshalEngine.Uniqueify("selfProto", identifiersUsed); identifiersUsed.Add(protoName); var selfProtoDecl = new SLDeclaration(false, new SLBinding(protoName, new SLIdentifier("self"), new SLSimpleType(func.ParameterLists [0] [0].TypeName.NameWithoutModule())), Visibility.None); preMarshalCode.Add(new SLLine(selfProtoDecl)); closureArgs.Insert(0, new SLAddressOf(new SLIdentifier(protoName), false)); } else { // add self as a parameter imports.AddIfNotPresent("XamGlue"); closureArgs.Insert(0, new SLFunctionCall("toIntPtr", false, new SLArgument(new SLIdentifier("value"), new SLIdentifier("self"), true))); } string throwReturnName = null; SLIdentifier throwReturn = null; SLType throwReturnType = null; throwReturnType = new SLTupleType( new SLNameTypePair(SLParameterKind.None, "_", func.ReturnTypeSpec == null || func.ReturnTypeSpec.IsEmptyTuple ? SLSimpleType.Void : typeMapper.TypeSpecMapper.MapType(func, imports, func.ReturnTypeSpec, true)), new SLNameTypePair(SLParameterKind.None, "_", new SLSimpleType("Swift.Error")), new SLNameTypePair(SLParameterKind.None, "_", new SLSimpleType("Bool"))); if (func.HasThrows) { throwReturnName = MarshalEngine.Uniqueify("retval", identifiersUsed); identifiersUsed.Add(throwReturnName); throwReturn = new SLIdentifier(throwReturnName); imports.AddIfNotPresent("XamGlue"); // FIXME for generics var throwReturnDecl = new SLDeclaration(true, new SLBinding(throwReturn, new SLFunctionCall(String.Format("UnsafeMutablePointer<{0}>.allocate", throwReturnType.ToString()), false, new SLArgument(new SLIdentifier("capacity"), SLConstant.Val(1), true))), Visibility.None); closureArgs.Insert(0, new SLFunctionCall("toIntPtr", false, new SLArgument(new SLIdentifier("value"), throwReturn, true))); preMarshalCode.Add(new SLLine(throwReturnDecl)); } if (func.HasThrows) { returnLine = new SLLine(new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs))); string errName = MarshalEngine.Uniqueify("err", identifiersUsed); identifiersUsed.Add(errName); var errIdent = new SLIdentifier(errName); var errDecl = new SLDeclaration(true, new SLBinding(errIdent, new SLFunctionCall("getExceptionThrown", false, new SLArgument(new SLIdentifier("retval"), throwReturn, true))), Visibility.None); postMarshalCode.Add(new SLLine(errDecl)); var ifblock = new SLCodeBlock(null); SLCodeBlock elseblock = null; ifblock.Add(SLFunctionCall.FunctionCallLine($"{throwReturnName}.deinitialize", new SLArgument(new SLIdentifier("count"), SLConstant.Val(1), true))); ifblock.Add(SLFunctionCall.FunctionCallLine($"{throwReturnName}.deallocate")); ifblock.Add(new SLLine(new SLThrow(new SLPostBang(errIdent, false)))); if (func.ReturnTypeSpec != null && !func.ReturnTypeSpec.IsEmptyTuple) { elseblock = new SLCodeBlock(null); string retvalvalName = MarshalEngine.Uniqueify("retvalval", identifiersUsed); identifiersUsed.Add(retvalvalName); SLIdentifier retvalval = new SLIdentifier(retvalvalName); string tuplecracker = "getExceptionNotThrown"; var retvalvaldecl = new SLDeclaration(true, new SLBinding(retvalval, new SLFunctionCall(tuplecracker, false, new SLArgument(new SLIdentifier("retval"), throwReturn, true))), Visibility.None); elseblock.Add(new SLLine(retvalvaldecl)); elseblock.Add(SLFunctionCall.FunctionCallLine($"{throwReturnName}.deallocate")); ISLExpr returnExpr = new SLPostBang(retvalval, false); elseblock.Add(SLReturn.ReturnLine(returnExpr)); } postMarshalCode.Add(new SLIfElse(new SLBinaryExpr(BinaryOp.NotEqual, errIdent, SLConstant.Nil), ifblock, elseblock)); } else { if (func.ReturnTypeSpec == null || func.ReturnTypeSpec.IsEmptyTuple) { // On no return value // _vtable.entry!(args) // returnLine = new SLLine(new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs))); } else { if (TypeSpec.IsBuiltInValueType(func.ReturnTypeSpec)) { // on simple return types (Int, UInt, Bool, etc) // return _vtable.entry!(args) // var closureCall = new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs)); if (postMarshalCode.Count == 0) { returnLine = SLReturn.ReturnLine(closureCall); } else { returnIdent = new SLIdentifier(MarshalEngine.Uniqueify("retval", identifiersUsed)); identifiersUsed.Add(returnIdent.Name); var retvalDecl = new SLDeclaration(true, returnIdent, null, closureCall, Visibility.None); returnLine = new SLLine(retvalDecl); postMarshalCode.Add(SLReturn.ReturnLine(returnIdent)); } } else { if (func.IsTypeSpecGeneric(func.ReturnTypeSpec)) { imports.AddIfNotPresent("XamGlue"); // dealing with a generic here. // UnsafeMutablePointer<T> retval = UnsafeMutablePointer<T>.alloc(1) // someCall(toIntPtr(retval), ...) // T actualRetval = retval.move() // retval.dealloc(1) // return actualRetval returnIdent = new SLIdentifier(MarshalEngine.Uniqueify("retval", identifiersUsed)); identifiersUsed.Add(returnIdent.Name); Tuple <int, int> depthIndex = func.GetGenericDepthAndIndex(func.ReturnTypeSpec); var retvalDecl = new SLDeclaration(true, returnIdent, null, new SLFunctionCall(String.Format("UnsafeMutablePointer<{0}>.allocate", SLGenericReferenceType.DefaultNamer(depthIndex.Item1, depthIndex.Item2)), false, new SLArgument(new SLIdentifier("capacity"), SLConstant.Val(1), true)), Visibility.None); preMarshalCode.Add(new SLLine(retvalDecl)); closureArgs.Insert(0, new SLFunctionCall("toIntPtr", false, new SLArgument(new SLIdentifier("value"), returnIdent, true))); SLIdentifier actualReturnIdent = new SLIdentifier(MarshalEngine.Uniqueify("actualRetval", identifiersUsed)); identifiersUsed.Add(actualReturnIdent.Name); returnLine = new SLLine(new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs))); var actualRetvalDecl = new SLDeclaration(true, actualReturnIdent, null, new SLFunctionCall(String.Format("{0}.move", returnIdent.Name), false), Visibility.None); postMarshalCode.Add(new SLLine(actualRetvalDecl)); postMarshalCode.Add(SLFunctionCall.FunctionCallLine(String.Format("{0}.deallocate", returnIdent.Name))); postMarshalCode.Add(SLReturn.ReturnLine(actualReturnIdent)); } else if (NamedSpecIsClass(func.ReturnTypeSpec as NamedTypeSpec)) { // class (not struct or enum) return type is a pointer // if we have no post marshal code: // return fromIntPtr(_vtable.entry!(args)) // if we have post marshal code: // let retval:returnType = fromIntPtr(_vtable.entry!(args)) // ... post marshal code // return retval; imports.AddIfNotPresent("XamGlue"); SLBaseExpr callExpr = new SLFunctionCall("fromIntPtr", false, new SLArgument(new SLIdentifier("ptr"), new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs)), true)); if (postMarshalCode.Count > 0) { string retvalName = MarshalEngine.Uniqueify("retval", identifiersUsed); var retDecl = new SLDeclaration(true, retvalName, typeMapper.TypeSpecMapper.MapType(func, imports, func.ReturnTypeSpec, true), callExpr); returnLine = new SLLine(retDecl); postMarshalCode.Add(SLReturn.ReturnLine(new SLIdentifier(retvalName))); } else { returnLine = SLReturn.ReturnLine(callExpr); } } else { var entity = typeMapper.GetEntityForTypeSpec(func.ReturnTypeSpec); if (func.ReturnTypeSpec is NamedTypeSpec && entity == null) { throw new NotImplementedException($"Function {func.ToFullyQualifiedName (true)} has an unknown return type {func.ReturnTypeSpec.ToString ()}"); } if (entity?.EntityType == EntityType.TrivialEnum) { imports.AddIfNotPresent(entity.Type.Module.Name); var slSelf = new SLIdentifier($"{entity.Type.Name}.self"); SLBaseExpr callExpr = new SLFunctionCall("unsafeBitCast", false, new SLArgument(new SLIdentifier("_"), new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs)), false), new SLArgument(new SLIdentifier("to"), slSelf, true)); if (postMarshalCode.Count == 0) { returnLine = SLReturn.ReturnLine(callExpr); } else { returnIdent = new SLIdentifier(MarshalEngine.Uniqueify("retval", identifiersUsed)); identifiersUsed.Add(returnIdent.Name); var retvalDecl = new SLDeclaration(true, returnIdent, null, callExpr, Visibility.None); returnLine = new SLLine(retvalDecl); postMarshalCode.Add(SLReturn.ReturnLine(returnIdent)); } } else { switch (func.ReturnTypeSpec.Kind) { case TypeSpecKind.Closure: // let retval:CT = allocSwiftClosureToFunc_ARGS () // _vtable.entry!(retval, args) // let actualReturn = netFuncToSwiftClosure (retval.move()) // retval.deallocate() // return actualReturn var ct = func.ReturnTypeSpec as ClosureTypeSpec; var slct = new SLBoundGenericType("UnsafeMutablePointer", ToMarshaledClosureType(func, ct)); var ptrName = MarshalEngine.Uniqueify("retval", identifiersUsed); identifiersUsed.Add(ptrName); var ptrAllocCallSite = ct.HasReturn() ? $"allocSwiftClosureToFunc_{ct.ArgumentCount()}" : $"allocSwiftClosureToAction_{ct.ArgumentCount ()}"; var ptrAllocCall = new SLFunctionCall(ptrAllocCallSite, false); var ptrDecl = new SLDeclaration(true, new SLIdentifier(ptrName), slct, ptrAllocCall, Visibility.None); preMarshalCode.Add(new SLLine(ptrDecl)); closureArgs.Insert(0, new SLIdentifier(ptrName)); returnLine = new SLLine(new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs))); var actualReturnName = MarshalEngine.Uniqueify("actualReturn", identifiersUsed); identifiersUsed.Add(actualReturnName); var convertCallSite = ct.HasReturn() ? "netFuncToSwiftClosure" : "netActionToSwiftClosure"; var isEmptyClosure = !ct.HasReturn() && !ct.HasArguments(); var pointerMove = new SLFunctionCall($"{ptrName}.move", false); var convertCall = isEmptyClosure ? pointerMove : new SLFunctionCall(convertCallSite, false, new SLArgument(new SLIdentifier("a1"), pointerMove, true)); var actualDecl = new SLDeclaration(true, actualReturnName, value: convertCall, vis: Visibility.None); postMarshalCode.Add(new SLLine(actualDecl)); postMarshalCode.Add(new SLReturn(new SLIdentifier(actualReturnName))); break; case TypeSpecKind.ProtocolList: case TypeSpecKind.Tuple: case TypeSpecKind.Named: var namedReturn = func.ReturnTypeSpec as NamedTypeSpec; // enums and structs can't get returned directly // instead they will be inserted at the head of the argument list // let retval = UnsafeMutablePointer<StructOrEnumType>.allocate(capacity: 1) // _vtable.entry!(retval, args) // T actualRetval = retval.move() // retval.deallocate() // return actualRetval string allocCallSite = String.Format("UnsafeMutablePointer<{0}>.allocate", func.ReturnTypeName); if (namedReturn != null) { imports.AddIfNotPresent(namedReturn.Module); } string retvalName = MarshalEngine.Uniqueify("retval", identifiersUsed); identifiersUsed.Add(retvalName); var retDecl = new SLDeclaration(true, retvalName, null, new SLFunctionCall(allocCallSite, false, new SLArgument(new SLIdentifier("capacity"), SLConstant.Val(1), true)), Visibility.None); preMarshalCode.Add(new SLLine(retDecl)); closureArgs.Insert(0, new SLIdentifier(retvalName)); returnLine = new SLLine(new SLNamedClosureCall(callInvocation, new CommaListElementCollection <SLBaseExpr> (closureArgs))); SLIdentifier actualReturnIdent = new SLIdentifier(MarshalEngine.Uniqueify("actualRetval", identifiersUsed)); identifiersUsed.Add(actualReturnIdent.Name); var actualRetvalDecl = new SLDeclaration(true, actualReturnIdent, null, new SLFunctionCall(String.Format("{0}.move", retvalName), false), Visibility.None); postMarshalCode.Add(new SLLine(actualRetvalDecl)); postMarshalCode.Add(SLFunctionCall.FunctionCallLine( String.Format("{0}.deallocate", retvalName))); postMarshalCode.Add(SLReturn.ReturnLine(actualReturnIdent)); break; } } } } } } foreach (ICodeElement line in preMarshalCode) { yield return(line); } yield return(returnLine); foreach (ICodeElement line in postMarshalCode) { yield return(line); } }