public void AppendExport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression instance, Seq<JST.Statement> body, AppendCallExported appendCallExported) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); if (methodDef.TypeArity > 0) { env.Log(new InvalidInteropMessage(ctxt, "polymorphic methods cannot be exported")); throw new DefinitionException(); } if (typeDef.Arity > 0 && (methodDef.IsConstructor || methodDef.IsStatic)) { env.Log (new InvalidInteropMessage (ctxt, "higher-kinded types cannot export static methods or instance constructors")); throw new DefinitionException(); } CheckParameterAndReturnTypesAreImportableExportable(assemblyDef, typeDef, methodDef); var script = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheScriptProperty, true, false, ref script); var outer = typeDef.OuterPropertyOrEvent(methodDef.MethodSignature); if (!methodDef.IsStatic && methodDef.IsConstructor) { // Constructors if (methodDef.TypeArity > 0) throw new InvalidOperationException("invalid constructor"); } else if (outer != null) { var localScript = default(JST.Expression); var hasLocalScript = attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheScriptProperty, false, false, ref localScript); switch (outer.Flavor) { case CST.MemberDefFlavor.Event: { var eventDef = (CST.EventDef)outer; if (eventDef.Add != null && methodDef.Signature.Equals(eventDef.Add)) // Adder script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "add", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "add", script); else if (eventDef.Remove != null && methodDef.Signature.Equals(eventDef.Remove)) // Remover script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "remove", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "remove", script); else throw new InvalidOperationException(); break; } case CST.MemberDefFlavor.Property: { var propDef = (CST.PropertyDef)outer; if (propDef.Get != null && methodDef.Signature.Equals(propDef.Get)) // Getter script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "get", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "get", script); else if (propDef.Set != null && methodDef.Signature.Equals(propDef.Set)) // Setter script = hasLocalScript ? GetterSetterAdderRemoverNameFromMethod (assemblyDef, typeDef, methodDef, "set", localScript) : GetterSetterAdderRemoverNameFromPropertyEvent (assemblyDef, typeDef, methodDef, "set", script); else throw new InvalidOperationException(); break; } case CST.MemberDefFlavor.Field: case CST.MemberDefFlavor.Method: throw new InvalidOperationException("not a property or event"); default: throw new ArgumentOutOfRangeException(); } } else { // Normal methods script = RecaseMethod(assemblyDef, typeDef, methodDef, script); } script = PrefixName(assemblyDef, typeDef, methodDef, script, true); AppendFinalExport (nameSupply, rootId, assemblyDef, typeDef, methodDef, script, instance, body, appendCallExported); }
// Take account of: // - BindToPrototype // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private void AppendFinalExport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, JST.Expression instance, ISeq<JST.Statement> body, AppendCallExported appendCallExported) { if (script == null) throw new InvalidOperationException("expecting default script value"); var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); var isInstance = !methodDef.IsStatic && !methodDef.IsConstructor; if (isInstance) { if (typeDef.Style is CST.ValueTypeStyle) { env.Log(new InvalidInteropMessage(ctxt, "cannot export instance methods from value types")); throw new DefinitionException(); } } var inlineParams = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheInlineParamsArrayProperty, true, false, ref inlineParams); var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams; var isPassRoot = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassRootAsArgumentProperty, true, false, ref isPassRoot); var isProto = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheBindToPrototypeProperty, true, false, ref isProto); var isPassInstance = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassInstanceAsArgumentProperty, true, false, ref isPassInstance); var bindToInstance = isInstance && !isProto; if (bindToInstance != (instance != null)) throw new InvalidOperationException("expecting instance"); var captureThis = isInstance && !isPassInstance; var funcScript = script as JST.FunctionExpression; // Build the function to export var funcBody = new Seq<JST.Statement>(); var funcParameters = new Seq<JST.Identifier>(); var funcCallArgs = new Seq<JST.Expression>(); var funcArity = methodDef.Arity; if ((methodDef.IsConstructor && !methodDef.IsStatic) || captureThis) // unmanaged will not pass the instance funcArity--; if (lastArgIsParamsArray) // managed params args will be extracted from remainder of unmanaged arguments funcArity--; if (captureThis) funcCallArgs.Add(new JST.ThisExpression()); for (var i = 0; i < funcArity; i++) { var id = nameSupply.GenSym(); funcParameters.Add(id); funcCallArgs.Add(id.ToE()); } if (lastArgIsParamsArray) { var iId = nameSupply.GenSym(); var arrId = nameSupply.GenSym(); funcBody.Add(JST.Statement.Var(arrId, new JST.ArrayLiteral())); funcBody.Add (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(funcArity), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(Constants.arguments.ToE(), Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (arrId.ToE(), Constants.push, new JST.IndexExpression(Constants.arguments.ToE(), iId.ToE()))))); funcCallArgs.Add(arrId.ToE()); } appendCallExported(nameSupply, assemblyDef, typeDef, methodDef, funcBody, funcCallArgs); var func = new JST.FunctionExpression(funcParameters, new JST.Statements(funcBody)); // Export the above function if (funcScript != null) { var scriptArgs = new Seq<JST.Expression>(); if (isPassRoot) scriptArgs.Add(rootId.ToE()); if (bindToInstance) scriptArgs.Add(instance); scriptArgs.Add(func); if (funcScript.Parameters.Count != scriptArgs.Count) { env.Log(new InvalidInteropMessage(ctxt, "invalid function arity")); throw new DefinitionException(); } body.Add(new JST.ExpressionStatement(new JST.CallExpression(script, scriptArgs))); } else { if (bindToInstance) script = JST.Expression.Dot(instance, JST.Expression.ExplodePath(script)); EnsurePathExists(body, script, !bindToInstance); body.Add(JST.Statement.Assignment(script, func)); } }