コード例 #1
0
ファイル: InteropManager.cs プロジェクト: modulexcite/IL2JS
        // Take acccount of:
        //   - PassRootAsArgument
        //   - PassInstanceAsArgument
        //   - InlineParamsArray
        private JST.Expression AppendFinalImport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, ISeq<JST.Statement> body, IImSeq<JST.Expression> arguments)
        {
            var isInstanceMethod = !(methodDef.IsStatic || methodDef.IsConstructor);
            var scriptExpectsRoot = default(bool);
            attributeHelper.GetValueFromMethod
                (assemblyDef,
                 typeDef,
                 methodDef,
                 attributeHelper.ImportAttributeRef,
                 attributeHelper.ThePassRootAsArgumentProperty,
                 true,
                 false,
                 ref scriptExpectsRoot);
            var passInstAsArg = default(bool);
            attributeHelper.GetValueFromMethod
                (assemblyDef,
                 typeDef,
                 methodDef,
                 attributeHelper.ImportAttributeRef,
                 attributeHelper.ThePassInstanceAsArgumentProperty,
                 true,
                 false,
                 ref passInstAsArg);
            var instanceIsThis = isInstanceMethod && !passInstAsArg;
            var inlineParams = default(bool);
            attributeHelper.GetValueFromMethod
                (assemblyDef,
                 typeDef,
                 methodDef,
                 attributeHelper.ImportAttributeRef,
                 attributeHelper.TheInlineParamsArrayProperty,
                 true,
                 false,
                 ref inlineParams);
            var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams;

            var funcScript = script as JST.FunctionExpression;

            var nextArg = 0;
            var instArg = default(JST.Expression);
            if (instanceIsThis)
            {
                // Instance argument will be the first arg to 'call' or 'apply', or the target of a '.' call.
                instArg = arguments[nextArg++];
                if (lastArgIsParamsArray && !instArg.IsDuplicatable)
                {
                    // Make sure instance argument is evaluated before the remaining arguments
                    var instId = nameSupply.GenSym();
                    body.Add(JST.Statement.Var(instId, instArg));
                    instArg = instId.ToE();
                }
            }
            else
            {
                if (lastArgIsParamsArray)
                    instArg = new JST.NullExpression();
            }

            var knownArgs = 0;
            var call = default(JST.Expression);

            if (lastArgIsParamsArray)
            {
                // We mush build script args at runtime
                var argsId = nameSupply.GenSym();
                body.Add(JST.Statement.Var(argsId, new JST.ArrayLiteral()));
                if (scriptExpectsRoot)
                {
                    body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, rootId.ToE()));
                    knownArgs++;
                }
                while (nextArg < arguments.Count - 1)
                {
                    body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, arguments[nextArg++]));
                    knownArgs++;
                }
                var arrArg = arguments[nextArg];
                if (!arrArg.IsDuplicatable)
                {
                    var arrId = nameSupply.GenSym();
                    body.Add(JST.Statement.Var(arrId, arrArg));
                    arrArg = arrId.ToE();
                }
                var iId = nameSupply.GenSym();
                body.Add
                    (new JST.IfStatement
                         (JST.Expression.IsNotNull(arrArg),
                          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(arrArg, Constants.length)),
                                         new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)),
                                    new JST.Statements
                                        (JST.Statement.DotCall
                                             (argsId.ToE(), Constants.push, new JST.IndexExpression(arrArg, iId.ToE())))))));

                if (funcScript != null)
                {
                    // var args = ...; var x = script; x.apply(this/null, args)
                    var scriptId = nameSupply.GenSym();
                    body.Add(JST.Statement.Var(scriptId, funcScript));
                    call = JST.Expression.DotCall(scriptId.ToE(), Constants.apply, instArg, argsId.ToE());
                }
                else
                {
                    if (instanceIsThis)
                    {
                        // var args = ...; (this.script).apply(this, args);
                        call = JST.Expression.DotCall
                            (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)),
                             Constants.apply,
                             instArg,
                             argsId.ToE());
                    }
                    else
                    {
                        // var args = ...; script.apply(null, args)
                        call = JST.Expression.DotCall(script, Constants.apply, instArg, argsId.ToE());
                    }
                }
            }
            else
            {
                var callArgs = new Seq<JST.Expression>();
                if (instanceIsThis && funcScript != null)
                    callArgs.Add(instArg);
                if (scriptExpectsRoot)
                {
                    callArgs.Add(rootId.ToE());
                    knownArgs++;
                }
                while (nextArg < arguments.Count)
                {
                    callArgs.Add(arguments[nextArg++]);
                    knownArgs++;
                }
                if (instanceIsThis)
                {
                    if (funcScript != null)
                    {
                        // var x = script; x.call(this, arg1, ..., argn)
                        var scriptId = nameSupply.GenSym();
                        body.Add(JST.Statement.Var(scriptId, funcScript));
                        call = JST.Expression.DotCall(scriptId.ToE(), Constants.call, callArgs);
                    }
                    else
                        // this.script(arg1, ..., angn)
                        call = new JST.CallExpression
                            (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)), callArgs);
                }
                else
                    // script(arg1, ..., argn)
                    call = new JST.CallExpression(script, callArgs);
            }

            if (funcScript != null)
            {
                if (funcScript.Parameters.Count < knownArgs)
                {
                    var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef);
                    env.Log(new InvalidInteropMessage(ctxt, "script accepts too few arguments"));
                    throw new DefinitionException();
                }
            }

            return call;
        }
コード例 #2
0
ファイル: InteropManager.cs プロジェクト: modulexcite/IL2JS
        // 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));
            }
        }