// Take account of: // - BindToPrototype // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private ExportMethodInfo FinalExportInfo(MessageContext ctxt, Func<JST.Identifier> gensym, JST.Identifier rootId, CCI.Method methodDefn, JST.Expression script) { if (script == null) throw new InvalidOperationException("expecting default script value"); var isInstance = !methodDefn.IsStatic && !(methodDefn is CCI.InstanceInitializer); if (isInstance) { var declType = methodDefn.DeclaringType; if (declType.IsValueType) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "cannot export instance methods from value types")); throw new DefinitionException(); } } var lastArgIsParamsArray = LastArgIsParamsArray(ctxt, methodDefn) && interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.TheInlineParamsArrayProperty); var isPassRoot = interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.ThePassRootAsArgumentProperty); var isProto = interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.TheBindToPrototypeProperty); var isPassInstance = interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.ThePassInstanceAsArgumentProperty); var bindToInstance = isInstance && !isProto; var captureThis = isInstance && !isPassInstance; var expectedScriptArity = (isPassRoot ? 1 : 0) + (bindToInstance ? 1 : 0) + 1; CheckScriptArity(ctxt, methodDefn, script, expectedScriptArity); var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); var callArgs = new Seq<JST.Expression>(); if (isPassRoot) callArgs.Add(rootId.ToE()); var instArgId = default(JST.Identifier); if (bindToInstance) { instArgId = gensym(); parameters.Add(instArgId); callArgs.Add(instArgId.ToE()); } var funcArgId = gensym(); parameters.Add(funcArgId); if (captureThis || lastArgIsParamsArray) { var innerParameters = new Seq<JST.Identifier>(); var innerBody = new Seq<JST.Statement>(); var innerArgs = new Seq<JST.Expression>(); var methodArity = Arity(methodDefn); for (var i = 0; i < methodArity; i++) { if (i == 0 && captureThis) innerArgs.Add(new JST.ThisExpression()); else if (i == methodArity - 1 && lastArgIsParamsArray) { var iId = gensym(); var arrId = gensym(); innerBody.Add(JST.Statement.Var(arrId, new JST.ArrayLiteral())); innerBody.Add (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(methodArity - 1), 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()))))); innerArgs.Add(arrId.ToE()); } else { var innerArgId = gensym(); innerParameters.Add(innerArgId); innerArgs.Add(innerArgId.ToE()); } } if (ReturnType(methodDefn) == null) { innerBody.Add(JST.Statement.Call(funcArgId.ToE(), innerArgs)); innerBody.Add(new JST.ReturnStatement()); } else innerBody.Add(new JST.ReturnStatement(new JST.CallExpression(funcArgId.ToE(), innerArgs))); var innerFunction = new JST.FunctionExpression(innerParameters, new JST.Statements(innerBody)); callArgs.Add(innerFunction); } else callArgs.Add(funcArgId.ToE()); if (script is JST.FunctionExpression) body.Add(new JST.ExpressionStatement(new JST.CallExpression(script, callArgs))); else { var i = 0; if (bindToInstance) script = JST.Expression.Dot(callArgs[i++], JST.Expression.ExplodePath(script)); EnsurePathExists(body, script, !bindToInstance); body.Add(JST.Statement.Assignment(script, callArgs[i])); } function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ExportMethodInfo { MethodDefn = methodDefn, Script = function, BindToInstance = bindToInstance }; }
// Take acccount of // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private ImportMethodInfo FinalImportScript(MessageContext ctxt, Func<JST.Identifier> gensym, JST.Identifier rootId, CCI.Method methodDefn, JST.Expression script, bool isNew) { if (script == null) throw new InvalidOperationException("expecting default script value"); var lastArgIsParamsArray = LastArgIsParamsArray(ctxt, methodDefn) && interopTypes.GetValue(ctxt, methodDefn, env.ImportAttributeType, interopTypes.TheInlineParamsArrayProperty); var methodArity = Arity(methodDefn); var isInstanceMethod = !(methodDefn.IsStatic || methodDefn is CCI.InstanceInitializer); var scriptExpectsRoot = interopTypes.GetValue (ctxt, methodDefn, env.ImportAttributeType, interopTypes.ThePassRootAsArgumentProperty); var instanceIsThis = isInstanceMethod && !interopTypes.GetValue (ctxt, methodDefn, env.ImportAttributeType, interopTypes.ThePassInstanceAsArgumentProperty); var expectedScriptArity = methodArity - (lastArgIsParamsArray ? 1 : 0) + (scriptExpectsRoot ? 1 : 0) - (instanceIsThis ? 1 : 0); CheckScriptArity(ctxt, methodDefn, script, expectedScriptArity); var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); var callArgs = new Seq<JST.Expression>(); if (lastArgIsParamsArray) { var argsId = gensym(); body.Add(JST.Statement.Var(argsId, new JST.ArrayLiteral())); if (scriptExpectsRoot) body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, rootId.ToE())); if (!isInstanceMethod) callArgs.Add(new JST.NullExpression()); for (var i = 0; i < methodArity; i++) { var id = gensym(); parameters.Add(id); if (isInstanceMethod && i == 0) { if (instanceIsThis) callArgs.Add(id.ToE()); else { callArgs.Add(new JST.NullExpression()); body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, id.ToE())); } } else if (i == methodArity - 1) { var iId = gensym(); body.Add (new JST.IfStatement (JST.Expression.IsNotNull(id.ToE()), new JST.Statements (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(0), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(id.ToE(), Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (argsId.ToE(), Constants.push, new JST.IndexExpression(id.ToE(), iId.ToE()))))))); } else body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, id.ToE())); } if (script is JST.FunctionExpression) { var funcId = gensym(); body.Add(JST.Statement.Var(funcId, script)); script = JST.Expression.Dot(funcId.ToE(), Constants.apply); } else script = JST.Expression.Dot(script, Constants.apply); callArgs.Add(argsId.ToE()); } else { if (scriptExpectsRoot) callArgs.Add(rootId.ToE()); for (var i = 0; i < methodArity; i++) { var id = gensym(); parameters.Add(id); if (i == 0 && instanceIsThis) { if (script is JST.FunctionExpression) { callArgs.Insert(0, id.ToE()); var funcId = gensym(); body.Add(JST.Statement.Var(funcId, script)); script = JST.Expression.Dot(funcId.ToE(), Constants.call); } else script = JST.Expression.Dot(id.ToE(), JST.Expression.ExplodePath(script)); } else callArgs.Add(id.ToE()); } } var exp = (JST.Expression)new JST.CallExpression(script, callArgs); if (isNew) exp = new JST.NewExpression(exp); if (ReturnType(methodDefn) == null) body.Add(new JST.ExpressionStatement(exp)); else body.Add(new JST.ReturnStatement(exp)); function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ImportMethodInfo { MethodDefn = methodDefn, Script = function }; }