Esempio n. 1
0
            public override JSExpression Translate(ILBlockTranslator translator, JSExpression[] arguments)
            {
                var thisArgument = FixupThisArgument(arguments[1], translator.TypeSystem);

                var returnType = ReturnType;

                if (returnType == null)
                {
                    returnType = translator.TypeSystem.Void;
                }

                var argumentValues = arguments.Skip(2).ToArray();
                var memberName     = MemberName;

                if ((TypeArguments != null) && (TypeArguments.Length > 0))
                {
                    memberName += "`" + TypeArguments.Length;
                }

                return(JSInvocationExpression.InvokeMethod(
                           new JSFakeMethod(
                               memberName, returnType,
                               (from av in argumentValues select av.GetActualType(translator.TypeSystem)).ToArray(),
                               translator.MethodTypes, TypeArguments
                               ), thisArgument,
                           arguments.Skip(2).ToArray()
                           ));
            }
        public JSStatement MaybeReplaceInvocation (JSInvocationExpression invocation) {
            bool isInControlFlow = Stack.OfType<JSStatement>().Any((n) => n.IsControlFlow);

            var jsm = invocation.JSMethod;
            if (
                (jsm != null) && 
                (jsm.Method.Name == ".ctor") && 
                TypeUtil.IsStruct(jsm.Method.DeclaringType.Definition)                
            ) {
                var previousInitialization = Initializations.LastOrDefault(
                    (ne) => 
                        ne.ParentBinaryExpression.Left.Equals(invocation.ThisReference)
                );

                if (previousInitialization != null) {
                    bool isDefaultInit = (previousInitialization.DefaultValueLiteral != null) ||
                        (previousInitialization.NewExpression.Arguments.Count == 0);

                    if (!previousInitialization.Folded && !isInControlFlow && isDefaultInit)
                        return FoldInvocation(invocation, previousInitialization);

                    if (jsm.Method.DeclaringType.IsImmutable) {
                        // A constructor is being invoked on an immutable struct, which means an existing instance is being mutated.
                        // We need to convert this into a reassignment of the local containing the struct. This creates garbage, but
                        //  it's probably still better than not applying immutability optimizations at all. :/
                        return ConvertInvocationIntoReassignment(invocation, previousInitialization);
                    }
                }
            }

            return null;
        }
Esempio n. 3
0
        public void VisitNode(JSBinaryOperatorExpression boe)
        {
            var leftType     = boe.Left.GetActualType(TypeSystem);
            var leftIsEnum   = IsEnumOrNullableEnum(leftType);
            var rightType    = boe.Right.GetActualType(TypeSystem);
            var rightIsEnum  = IsEnumOrNullableEnum(rightType);
            var resultType   = boe.GetActualType(TypeSystem);
            var resultIsEnum = IsEnumOrNullableEnum(resultType);

            if ((leftIsEnum || rightIsEnum) && LogicalOperators.Contains(boe.Operator))
            {
                if (leftIsEnum)
                {
                    var cast = JSInvocationExpression.InvokeStatic(
                        JS.Number(TypeSystem.Int32), new[] { boe.Left }, true
                        );

                    boe.ReplaceChild(boe.Left, cast);
                }

                if (rightIsEnum)
                {
                    var cast = JSInvocationExpression.InvokeStatic(
                        JS.Number(TypeSystem.Int32), new[] { boe.Right }, true
                        );

                    boe.ReplaceChild(boe.Right, cast);
                }
            }

            VisitChildren(boe);
        }
        public void VisitNode(JSNewArrayElementReference naer)
        {
            var isInsideLoop     = (Stack.Any((node) => node is JSLoopStatement));
            var parentPassByRef  = ParentNode as JSPassByReferenceExpression;
            var parentInvocation = Stack.OfType <JSInvocationExpression>().FirstOrDefault();
            var doesValueEscape  = DoesValueEscapeFromInvocation(parentInvocation, naer, true);

            if (
                isInsideLoop &&
                (parentPassByRef != null) &&
                (parentInvocation != null) &&
                !doesValueEscape
                )
            {
                var replacement = CreateHoistedVariable(
                    (hoistedVariable) => JSInvocationExpression.InvokeMethod(
                        new JSFakeMethod("retarget", hoistedVariable.GetActualType(TypeSystem), new TypeReference[] { TypeSystem.Object, TypeSystem.Int32 }, MethodTypes),
                        hoistedVariable, new JSExpression[] { naer.Array, naer.Index }
                        ),
                    naer.GetActualType(TypeSystem),
                    naer.MakeUntargeted()
                    );

                ParentNode.ReplaceChild(naer, replacement);
                VisitReplacement(replacement);
            }

            VisitChildren(naer);
        }
Esempio n. 5
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            var        jsm    = invocation.JSMethod;
            MethodInfo method = null;

            if (jsm != null)
            {
                method = jsm.Method;
            }

            bool isOverloaded = (method != null) &&
                                method.IsOverloadedRecursive &&
                                !method.Metadata.HasAttribute("JSIL.Meta.JSRuntimeDispatch");

            if (isOverloaded && JavascriptAstEmitter.CanUseFastOverloadDispatch(method))
            {
                isOverloaded = false;
            }

            if ((method != null) && method.DeclaringType.IsInterface)
            {
                // HACK
                if (!PackedArrayUtil.IsPackedArrayType(jsm.Reference.DeclaringType))
                {
                    CacheInterfaceMember(jsm.Reference.DeclaringType, jsm.Identifier);
                }
            }

            if ((jsm != null) && (method != null) && isOverloaded)
            {
                CacheSignature(jsm.Reference, method.Signature, false);
            }

            VisitChildren(invocation);
        }
Esempio n. 6
0
 public JSInvocationExpression GetTypeFromAssembly(JSExpression assembly, JSExpression typeName, JSExpression throwOnFail)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("GetTypeFromAssembly", TypeSystem.SystemType()),
                new[] { assembly, typeName, new JSNullLiteral(TypeSystem.Object), throwOnFail }, true
                ));
 }
Esempio n. 7
0
        private JSStatement FoldInvocation(JSInvocationExpression invocation, InitializationInfo ii)
        {
            var arguments     = invocation.Arguments.ToArray();
            var newExpression = new JSNewExpression(
                ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments
                );

            // Constructor call contains a reference to the struct being initialized.
            // For some reason the C# compiler lets you do this even though it would be undefined
            //  if not for a nuance in how struct locals work in MSIL.
            if (newExpression.SelfAndChildrenRecursive.Any((n) => n.Equals(invocation.ThisReference)))
            {
                return(null);
            }

            ii.Folded = true;
            ii.BinaryExpressionParent.ReplaceChild(ii.ParentBinaryExpression, new JSNullExpression());

            var newBoe = new JSBinaryOperatorExpression(
                JSOperator.Assignment,
                ii.ParentBinaryExpression.Left, newExpression,
                ii.ParentBinaryExpression.ActualType
                );

            return(new JSVariableDeclarationStatement(newBoe));
        }
Esempio n. 8
0
 public JSInvocationExpression GetTypeOf(JSExpression expression)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("GetType", TypeSystem.SystemType()),
                new[] { expression }, true
                ));
 }
        public void VisitNode(JSNewExpression newexp)
        {
            var type = newexp.GetActualType(TypeSystem);

            var isStruct         = TypeUtil.IsStruct(type);
            var isInsideLoop     = (Stack.Any((node) => node is JSLoopStatement));
            var parentInvocation = ParentNode as JSInvocationExpression;
            var doesValueEscape  = DoesValueEscapeFromInvocation(parentInvocation, newexp, false);

            if (isStruct &&
                isInsideLoop &&
                (parentInvocation != null) &&
                !doesValueEscape
                )
            {
                var replacement = CreateHoistedVariable(
                    (hoistedVariable) => new JSCommaExpression(
                        JSInvocationExpression.InvokeMethod(
                            type, new JSMethod(newexp.ConstructorReference, newexp.Constructor, MethodTypes, null), hoistedVariable,
                            newexp.Arguments.ToArray(), false
                            ),
                        hoistedVariable
                        ),
                    type
                    );

                ParentNode.ReplaceChild(newexp, replacement);
                VisitReplacement(replacement);
            }
            else
            {
                VisitChildren(newexp);
            }
        }
Esempio n. 10
0
        public static JSExpression GetItem(TypeReference targetType, TypeInfo targetTypeInfo, JSExpression target, JSExpression index, MethodTypeFactory methodTypes, bool proxy = false)
        {
            var targetGit     = (GenericInstanceType)targetType;
            var getMethodName = "get_Item";
            var getMethod     = (JSIL.Internal.MethodInfo)targetTypeInfo.Members.First(
                (kvp) => kvp.Key.Name == getMethodName
                ).Value;

            if (proxy)
            {
                return(new JSNewPackedArrayElementProxy(
                           target, index, targetGit.GenericArguments[0]
                           ));
            }

            // We have to construct a custom reference to the method in order for ILSpy's
            //  SubstituteTypeArgs method not to explode later on
            var getMethodReference = CecilUtil.RebindMethod(getMethod.Member, targetGit, targetGit.GenericArguments[0]);

            var result = JSInvocationExpression.InvokeMethod(
                new JSType(targetType),
                new JSMethod(getMethodReference, getMethod, methodTypes),
                target,
                new JSExpression[] { index },
                constantIfArgumentsAre: proxy
                );

            return(result);
        }
Esempio n. 11
0
        public void VisitNode(JSUnaryOperatorExpression uoe)
        {
            var exType = uoe.Expression.GetActualType(TypeSystem);
            var opType = uoe.ActualType;

            if (IsLongOrULong(exType) && IsLongOrULong(opType)) //exType == TypeSystem.Int64 && opType == TypeSystem.Int64)
            {
                string verb;
                switch (uoe.Operator.Token)
                {
                case "-":
                    verb = "op_UnaryNegation";
                    break;

                case "~":
                    verb = "op_OnesComplement";
                    break;

                default:
                    throw new NotSupportedException();
                }

                var method      = new JSFakeMethod(verb, TypeSystem.Int64, new[] { TypeSystem.Int64 }, MethodTypeFactory);
                var replacement = JSInvocationExpression.InvokeStatic(exType, method, new[] { uoe.Expression }, true);
                ParentNode.ReplaceChild(uoe, replacement);
                VisitReplacement(replacement);
                return;
            }

            VisitChildren(uoe);
        }
Esempio n. 12
0
 public JSExpression Cast(JSExpression expression, TypeReference targetType)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("Cast", targetType),
                new[] { expression, new JSTypeOfExpression(targetType) }, true
                ));
 }
Esempio n. 13
0
 public JSInvocationExpression StructEquals(JSExpression left, JSExpression right)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("StructEquals", TypeSystem.Boolean),
                new[] { left, right }, true
                ));
 }
Esempio n. 14
0
 protected JSInvocationExpression CastToInteger(JSExpression booleanExpression)
 {
     return(JSInvocationExpression.InvokeMethod(
                JS.valueOf(TypeSystem.SByte),
                booleanExpression, null, true
                ));
 }
Esempio n. 15
0
 public JSInvocationExpression GetTypeOf(JSExpression expression)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("GetType", new TypeReference("System", "Type", TypeSystem.Object.Module, TypeSystem.Object.Scope)),
                new[] { expression }, true
                ));
 }
Esempio n. 16
0
        public void VisitNode(JSInvocationExpression ie)
        {
            var jsm = ie.JSMethod;

            if (jsm == null)
            {
                Console.WriteLine("Can't translate non-JSMethod {0}", ie);
                Formatter.WriteSExpr("untranslatable.call");
                return;
            }
            else if (!jsm.Method.IsStatic)
            {
                Console.WriteLine("Can't translate instance call {0}", ie);
                Formatter.WriteSExpr("untranslatable.call");
                return;
            }

            var methodDef  = jsm.Reference.Resolve();
            var memberName = WasmUtil.FormatMemberName(methodDef);

            Formatter.WriteSExpr("call", (_) => {
                _.WriteRaw("${0} ", memberName);
                EmitArgumentList(_, ie.Arguments, false);
            });
        }
Esempio n. 17
0
 public JSInvocationExpression FreezeImmutableObject(JSExpression @object)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot(new JSFakeMethod("FreezeImmutableObject", TypeSystem.Void, new[] { TypeSystem.Object }, MethodTypes)),
                new[] { @object }
                ));
 }
Esempio n. 18
0
 public JSInvocationExpression CreateInstanceOfType(TypeReference type)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot(new JSFakeMethod("CreateInstanceOfType", type, new[] { TypeSystem.Object }, MethodTypes)),
                new[] { new JSTypeOfExpression(type) }
                ));
 }
Esempio n. 19
0
        public void InvocationChildNames()
        {
            var ie = JSInvocationExpression.InvokeMethod(
                new JSStringIdentifier("Method"),
                new JSStringIdentifier("ThisReference"),
                new JSExpression[] {
                new JSStringIdentifier("Argument")
            }
                );

            var pairs = new List <KeyValuePair <JSNode, string> >();

            using (var e = ie.Children.EnumeratorTemplate)
                while (e.MoveNext())
                {
                    pairs.Add(new KeyValuePair <JSNode, string>(e.Current, e.CurrentName));
                }

            Assert.AreEqual(4, pairs.Count);

            Assert.AreEqual(ie.Method, pairs[1].Key);
            Assert.AreEqual("Method", pairs[1].Value);

            Assert.AreEqual(ie.ThisReference, pairs[2].Key);
            Assert.AreEqual("ThisReference", pairs[2].Value);
        }
Esempio n. 20
0
 public JSInvocationExpression ThrowNullReferenceException()
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot(new JSFakeMethod("ThrowNullReferenceException", TypeSystem.Void, new TypeReference[0], MethodTypes)),
                new JSExpression[0]
                ));
 }
Esempio n. 21
0
 public JSInvocationExpression GetTypeFromAssembly(JSExpression assembly, JSExpression typeName, JSExpression throwOnFail)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("GetTypeFromAssembly", new TypeReference("System", "Type", TypeSystem.Object.Module, TypeSystem.Object.Scope)),
                new[] { assembly, typeName, new JSNullLiteral(TypeSystem.Object), throwOnFail }, true
                ));
 }
Esempio n. 22
0
        public void VisitNode(JSInvocationExpression ie)
        {
            var variables = ExtractAffectedVariables(ie.Method, ie.Parameters);

            var type    = ie.JSType;
            var thisVar = ExtractAffectedVariable(ie.ThisReference);
            var method  = ie.JSMethod;

            if (thisVar != null)
            {
                ModifiedVariable(thisVar);

                State.Invocations.Add(new FunctionAnalysis1stPass.Invocation(
                                          GetParentNodeIndices(), StatementIndex, NodeIndex, thisVar, method, ie.Method, variables
                                          ));
            }
            else
            {
                State.Invocations.Add(new FunctionAnalysis1stPass.Invocation(
                                          GetParentNodeIndices(), StatementIndex, NodeIndex, type, method, ie.Method, variables
                                          ));
            }

            VisitChildren(ie);
        }
Esempio n. 23
0
 public JSInvocationExpression CheckType(JSExpression expression, TypeReference targetType)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("CheckType", TypeSystem.Boolean),
                new[] { expression, new JSTypeOfExpression(targetType) }, true
                ));
 }
Esempio n. 24
0
 public JSInvocationExpression Coalesce(JSExpression left, JSExpression right, TypeReference expectedType)
 {
     return(JSInvocationExpression.InvokeStatic(
                Dot("Coalesce", expectedType),
                new[] { left, right }, true
                ));
 }
Esempio n. 25
0
 public JSInvocationExpression ShallowCopy(JSExpression array, JSExpression initializer, TypeReference arrayType)
 {
     return(JSInvocationExpression.InvokeStatic(
                new JSDotExpression(
                    Dot("Array", TypeSystem.Object),
                    new JSFakeMethod("ShallowCopy", TypeSystem.Void, new[] { arrayType, arrayType }, MethodTypes)
                    ), new[] { array, initializer }
                ));
 }
Esempio n. 26
0
 public JSInvocationExpression NewDelegate(TypeReference delegateType, JSExpression thisReference, JSExpression targetMethod)
 {
     return(JSInvocationExpression.InvokeStatic(
                new JSDotExpression(
                    new JSType(delegateType),
                    new JSFakeMethod("New", delegateType, new[] { TypeSystem.Object, TypeSystem.Object }, MethodTypes)
                    ), new [] { thisReference, targetMethod },
                true
                ));
 }
Esempio n. 27
0
        public JSInvocationExpression NewDelegate(TypeReference delegateType, JSExpression thisReference, JSExpression targetMethod)
        {
            var targetDotExpression = targetMethod as JSDotExpressionBase;
            var jsMethod            = targetDotExpression != null ? targetDotExpression.Member as JSMethod : null;

            JSExpression[] invocationExpressionArguments;

            if (jsMethod == null)
            {
                // Not sure if it is possible.
                invocationExpressionArguments = new[] { thisReference, targetMethod };
            }
            else
            {
                var jsMethodAccess = targetMethod as JSMethodAccess;
                var arguments      = jsMethod == null
                    ? null
                    : jsMethod.Reference.Parameters.Select(
                    (parameter, index) => (JSExpression) new JSRawOutputIdentifier(parameter.ParameterType, "arguments[{0}]", index))
                                     .ToArray();

                bool isFromDelegate = jsMethod.Reference.Name == "Invoke" && TypeUtil.IsDelegateType(jsMethod.Reference.DeclaringType);

                JSExpression methodInvocation;
                if (isFromDelegate)
                {
                    methodInvocation = targetMethod;
                }
                else if (jsMethod.Method.IsStatic)
                {
                    methodInvocation = new JSDeferredExpression(JSInvocationExpression.InvokeStatic(jsMethod.Reference.DeclaringType, jsMethod, arguments));
                }
                else if (jsMethodAccess == null || jsMethodAccess.IsVirtual)
                {
                    methodInvocation = new JSDeferredExpression(JSInvocationExpression.InvokeMethod(jsMethod.Reference.DeclaringType, jsMethod, thisReference, arguments));
                }
                else
                {
                    methodInvocation = new JSDeferredExpression(JSInvocationExpression.InvokeBaseMethod(jsMethod.Reference.DeclaringType, jsMethod, thisReference, arguments));
                }

                invocationExpressionArguments = new[] {
                    thisReference,
                    methodInvocation,
                    new JSDeferredExpression(new JSMethodOfExpression(jsMethod.Reference, jsMethod.Method, jsMethod.MethodTypes, jsMethod.GenericArguments))
                };
            }

            return(JSInvocationExpression.InvokeStatic(
                       new JSDotExpression(
                           new JSType(delegateType),
                           new JSFakeMethod("New", delegateType, new[] { TypeSystem.Object, TypeSystem.Object }, MethodTypes)),
                       invocationExpressionArguments,
                       true));
        }
Esempio n. 28
0
        public JSInvocationExpression ValueOfNullable(JSExpression nullableExpression)
        {
            var valueType = nullableExpression.GetActualType(TypeSystem);

            valueType = TypeUtil.StripNullable(valueType);

            return(JSInvocationExpression.InvokeStatic(
                       Dot("ValueOfNullable", valueType),
                       new[] { nullableExpression }, true
                       ));
        }
Esempio n. 29
0
        public JSExpression ValueOfNullableOrDefault(JSExpression nullableExpression, JSExpression defaultValue)
        {
            var valueType = nullableExpression.GetActualType(TypeSystem);

            valueType = TypeUtil.StripNullable(valueType);

            return(JSInvocationExpression.InvokeStatic(
                       Dot("Nullable_ValueOrDefault", valueType),
                       new[] { nullableExpression, defaultValue }, true
                       ));
        }
Esempio n. 30
0
        public void VisitNode(JSInvocationExpression ie)
        {
            if ((ie.GenericArguments != null) && (ie.JSMethod != null))
            {
                ie.JSMethod.SetCachedGenericArguments(
                    from ga in ie.GenericArguments select GetCachedType(ga)
                    );
            }

            VisitChildren(ie);
        }
Esempio n. 31
0
        public JSInvocationExpression StackAlloc(JSExpression sizeInBytes, TypeReference pointerType)
        {
            if (!pointerType.IsPointer)
            {
                throw new InvalidOperationException("Type being stack-allocated must be a pointer");
            }

            return(JSInvocationExpression.InvokeStatic(
                       Dot(new JSFakeMethod("StackAlloc", pointerType, new[] { TypeSystem.Int32, TypeSystem.Object }, MethodTypes)),
                       new[] { sizeInBytes, new JSType(TypeUtil.GetElementType(pointerType, true)) }
                       ));
        }
        public JSStatement MaybeReplaceInvocation(JSInvocationExpression invocation)
        {
            var jsm = invocation.JSMethod;
            if (
                (jsm != null) &&
                (jsm.Method.Name == ".ctor") &&
                TypeUtil.IsStruct(jsm.Method.DeclaringType.Definition) &&
                !Stack.OfType<JSBlockStatement>().Any((n) => n.IsControlFlow)
            ) {
                var previousInitialization = Initializations.LastOrDefault(
                    (ne) =>
                        ne.ParentBinaryExpression.Left.Equals(invocation.ThisReference)
                );

                if (previousInitialization != null)
                    return FoldInvocation(invocation, previousInitialization);
            }

            return null;
        }
Esempio n. 33
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            for (int i = 0, c = invocation.Arguments.Count; i < c; i++) {
                var argument = invocation.Arguments[i];

                if (IsCopyNeeded(argument)) {
                    if (Tracing)
                        Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument));
                    invocation.Arguments[i] = new JSStructCopyExpression(argument);
                }
            }

            VisitChildren(invocation);
        }
Esempio n. 34
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
                sa = FunctionSource.GetSecondPass(invocation.JSMethod);

            var parms = invocation.Parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++) {
                var pd = parms[i].Key;
                var argument = parms[i].Value;

                string parameterName = null;
                if (pd != null)
                    parameterName = pd.Name;

                if (IsParameterCopyNeeded(sa, parameterName, argument)) {
                    if (Tracing)
                        Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument));

                    invocation.Arguments[i] = new JSStructCopyExpression(argument);
                } else {
                    if (Tracing)
                        Debug.WriteLine(String.Format("struct copy elided for argument {0}", argument));
                }
            }

            VisitChildren(invocation);
        }
Esempio n. 35
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
                sa = FunctionSource.GetSecondPass(invocation.JSMethod);

            var parms = invocation.Parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++) {
                var pd = parms[i].Key;
                var argument = parms[i].Value;

                string parameterName = null;
                if (pd != null)
                    parameterName = pd.Name;

                GenericParameter relevantParameter;
                if (IsParameterCopyNeeded(sa, parameterName, argument, out relevantParameter)) {
                    if (Tracing)
                        Debug.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument));

                    invocation.Arguments[i] = MakeCopyForExpression(argument, relevantParameter);
                } else {
                    if (Tracing && TypeUtil.IsStruct(argument.GetActualType(TypeSystem)))
                        Debug.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument));
                }
            }

            var thisReference = invocation.ThisReference;
            if (
                (thisReference != null) &&
                (sa != null) &&
                sa.ViolatesThisReferenceImmutability &&
                !(ParentNode is JSCommaExpression)
            ) {
                // The method we're calling violates immutability so we need to clone the this-reference
                //  before we call it.
                var thisReferenceType = thisReference.GetActualType(TypeSystem);
                if (TypeUtil.IsStruct(thisReferenceType)) {
                    if (!(thisReference is JSVariable) && !(thisReference is JSFieldAccess))
                        throw new NotImplementedException("Unsupported invocation of method that reassigns this within an immutable struct: " + invocation.ToString());

                    var cloneExpr = new JSBinaryOperatorExpression(
                        JSOperator.Assignment, thisReference, new JSStructCopyExpression(thisReference), thisReferenceType
                    );
                    var commaExpression = new JSCommaExpression(cloneExpr, invocation);

                    ParentNode.ReplaceChild(invocation, commaExpression);
                    VisitReplacement(commaExpression);
                    return;
                }
            }

            VisitChildren(invocation);
        }
        private JSStatement FoldInvocation(JSInvocationExpression invocation, InitializationInfo ii)
        {
            var arguments = invocation.Arguments.ToArray();
            var newExpression = new JSNewExpression(
                ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments
            );

            ii.BinaryExpressionParent.ReplaceChild(ii.ParentBinaryExpression, new JSNullExpression());
            Initializations.Remove(ii);

            var newBoe = new JSBinaryOperatorExpression(
                JSOperator.Assignment,
                ii.ParentBinaryExpression.Left, newExpression,
                ii.ParentBinaryExpression.ActualType
            );

            return new JSVariableDeclarationStatement(newBoe);
        }
Esempio n. 37
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
                sa = FunctionSource.GetSecondPass(invocation.JSMethod);

            var parms = invocation.Parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++) {
                var pd = parms[i].Key;
                var argument = parms[i].Value;

                if (IsCopyNeeded(argument)) {
                    bool modified = true, escapes = true, isResult = false;

                    if ((sa != null) && (pd != null)) {
                        var varName = pd.Name;
                        modified = sa.ModifiedVariables.Contains(varName);
                        escapes = sa.EscapingVariables.Contains(varName);
                        isResult = sa.ResultVariable == varName;
                    }

                    if (modified || (escapes && !isResult)) {
                        if (Tracing)
                            Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument));

                        invocation.Arguments[i] = new JSStructCopyExpression(argument);
                    } else {
                        if (Tracing)
                            Debug.WriteLine(String.Format("struct copy elided for argument {0}", argument));
                    }
                }
            }

            VisitChildren(invocation);
        }
        public void VisitNode (JSInvocationExpression invocation) {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
                sa = GetSecondPass(invocation.JSMethod);

            CloneArgumentsIfNecessary(invocation.Parameters, invocation.Arguments, sa);

            var thisReference = invocation.ThisReference;
            if (
                (thisReference != null) && 
                (sa != null) && 
                sa.ViolatesThisReferenceImmutability && 
                !(ParentNode is JSCommaExpression)
            ) {
                // The method we're calling violates immutability so we need to clone the this-reference
                //  before we call it.
                var thisReferenceType = thisReference.GetActualType(TypeSystem);
                if (TypeUtil.IsStruct(thisReferenceType)) {
                    if (!(thisReference is JSVariable) && !(thisReference is JSFieldAccess))
                        throw new NotImplementedException("Unsupported invocation of method that reassigns this within an immutable struct: " + invocation.ToString());

                    var cloneExpr = new JSBinaryOperatorExpression(
                        JSOperator.Assignment, thisReference, new JSStructCopyExpression(thisReference), thisReferenceType
                    );
                    var commaExpression = new JSCommaExpression(cloneExpr, invocation);

                    ParentNode.ReplaceChild(invocation, commaExpression);
                    VisitReplacement(commaExpression);
                    return;
                }
            }

            VisitChildren(invocation);
        }
Esempio n. 39
0
        public void VisitNode(JSInvocationExpression ie)
        {
            if ((ie.GenericArguments != null) && (ie.JSMethod != null)) {
                ie.JSMethod.SetCachedGenericArguments(
                    from ga in ie.GenericArguments select GetCachedType(ga)
                );
            }

            VisitChildren(ie);
        }
Esempio n. 40
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
                sa = FunctionSource.GetSecondPass(invocation.JSMethod);

            var parms = invocation.Parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++) {
                var pd = parms[i].Key;
                var argument = parms[i].Value;

                string parameterName = null;
                if (pd != null)
                    parameterName = pd.Name;

                GenericParameter relevantParameter;
                if (IsParameterCopyNeeded(sa, parameterName, argument, out relevantParameter)) {
                    if (Tracing)
                        Debug.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument));

                    invocation.Arguments[i] = MakeCopyForExpression(argument, relevantParameter);
                } else {
                    if (Tracing && TypeUtil.IsStruct(argument.GetActualType(TypeSystem)))
                        Debug.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument));
                }
            }

            VisitChildren(invocation);
        }
Esempio n. 41
0
        public void VisitNode (JSInvocationExpression invocation) {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
                sa = GetSecondPass(invocation.JSMethod);

            CloneArgumentsIfNecessary(invocation.Parameters, invocation.Arguments, sa);

            var thisReference = invocation.ThisReference;
            var thisReferenceType = thisReference.GetActualType(TypeSystem);
            var thisReferenceIsStruct = TypeUtil.IsStruct(thisReferenceType) && 
                !TypeUtil.IsNullable(thisReferenceType);

            bool thisReferenceNeedsCopy = false;
            bool thisReferenceNeedsCopyAndReassignment = false;

            if (thisReferenceIsStruct && !invocation.SuppressThisClone) {
                var isMethodInvocation = (thisReference != null) && 
                    (sa != null) && 
                    !(ParentNode is JSCommaExpression) &&
                    !(thisReference is JSStructCopyExpression) &&
                    !(
                        (ParentNode is JSResultReferenceExpression) &&
                        Stack.OfType<JSCommaExpression>().Any()
                    );


                // If a struct is immutable, a method may reassign the this-reference,
                //  i.e. 'this = otherstruct' successfully.
                // In this scenario we have to clone the old this-reference, invoke
                //  the method on the clone, and replace the old this-reference with
                //  the new, modified clone.

                thisReferenceNeedsCopyAndReassignment = isMethodInvocation && sa.ViolatesThisReferenceImmutability;


                // When invoking a method that mutates a struct's members or lets them escape,
                //  we need to copy the this-reference if it isn't writable.

                // FIXME: We're white-listing writable targets, but we probably want to blacklist
                //  non-writable targets instead, so that if a fn's result is new we don't clone it
                //  to use it as a this-reference.

                // FIXME: Handle pointers, replace x.get().foo() with some sort of comma expr,
                //  like ($x = x.get(), $x.foo(), x.set($x)) ?
                var isWritableInstance = 
                    (thisReference is JSFieldAccess) ||
                        (thisReference is JSVariable) ||
                        (thisReference is JSReadThroughReferenceExpression);

                thisReferenceNeedsCopy = isMethodInvocation &&
                    (
                        sa.IsVariableModified("this") || 
                        // Maybe don't include return here?
                        sa.DoesVariableEscape("this", true)
                    ) &&
                    (!isWritableInstance);
            }

            GenericParameter relevantParameter;
            var isCopyNeeded = IsCopyNeeded(thisReference, out relevantParameter, false);
            if (
                thisReferenceNeedsCopyAndReassignment && isCopyNeeded
            ) {
                if (TraceInsertedCopies)
                    Console.WriteLine("Cloning this-reference because method reassigns this: {0}", invocation);

                if (
                    (thisReference is JSFieldAccess) ||
                    (thisReference is JSVariable)
                ) {
                    var rre = ParentNode as JSResultReferenceExpression;
                    var cloneExpr = new JSBinaryOperatorExpression(
                        JSOperator.Assignment, thisReference, MakeCopyForExpression(thisReference, relevantParameter), thisReferenceType
                    );
                    var commaExpression = new JSCommaExpression(
                        cloneExpr,
                        (rre != null)
                            ? (JSExpression)new JSResultReferenceExpression(invocation)
                            : (JSExpression)invocation
                    );

                    if (rre != null) {
                        ResultReferenceReplacement = commaExpression;
                        return;
                    } else {
                        ParentNode.ReplaceChild(invocation, commaExpression);
                        VisitReplacement(commaExpression);
                        return;
                    }
                } else {
                    // Huh?
                    invocation.ReplaceChild(thisReference, MakeCopyForExpression(thisReference, relevantParameter));
                    VisitChildren(invocation);
                    return;
                }
            } else if (thisReferenceNeedsCopy && isCopyNeeded) {
                if (TraceInsertedCopies)
                    Console.WriteLine("Cloning this-reference because method mutates this and this-reference is not field/local: {0}", invocation);

                invocation.ReplaceChild(thisReference, MakeCopyForExpression(thisReference, relevantParameter));
                VisitChildren(invocation);
                return;
            }

            VisitChildren(invocation);            
        }
Esempio n. 42
0
        bool DoesValueEscapeFromInvocation (JSInvocationExpression invocation, JSExpression argumentExpression, bool includeReturn) {
            if (
                (invocation != null) &&
                (invocation.JSMethod != null) &&
                (invocation.JSMethod.Reference != null)
            ) {
                var methodDef = invocation.JSMethod.Reference.Resolve();
                var secondPass = FunctionSource.GetSecondPass(invocation.JSMethod, Function.Method.QualifiedIdentifier);
                if ((secondPass != null) && (methodDef != null)) {
                    // HACK
                    var argumentIndex = invocation.Arguments.Select(
                        (a, i) => new { argument = a, index = i })
                        .FirstOrDefault((_) => _.argument.SelfAndChildrenRecursive.Contains(argumentExpression));

                    if (argumentIndex != null) {
                        var argumentName = methodDef.Parameters[argumentIndex.index].Name;

                        return secondPass.DoesVariableEscape(argumentName, includeReturn);
                    }
                } else if (secondPass != null) {
                    // HACK for methods that do not have resolvable references. In this case, if NONE of their arguments escape, we're probably still okay.
                    if (secondPass.GetNumberOfEscapingVariables(includeReturn) == 0)
                        return false;
                }
            }

            return true;
        }
Esempio n. 43
0
 public void VisitNode(JSInvocationExpression ie)
 {
     var jsm = ie.JSMethod;
     // Detect direct invocations of getter methods
     if (
         (jsm != null) &&
         (jsm.Method.Property == Property.Property) &&
         (ie.ThisReference.Equals(ThisReference)) &&
         (ie.Arguments.Count == 0)
     ) {
         ParentNode.ReplaceChild(ie, Replacement);
         VisitReplacement(Replacement);
         ReplacedInvocation = true;
     } else {
         VisitChildren(ie);
     }
 }
Esempio n. 44
0
        public void VisitNode (JSInvocationExpression ie) {
            var type = ie.JSType;
            var method = ie.JSMethod;
            var thisExpression = ie.ThisReference;

            if (method != null) {
                if (
                    (type != null) &&
                    (type.Type.FullName == "System.Object")
                ) {
                    switch (method.Method.Member.Name) {
                        case ".ctor": {
                            var replacement = new JSNullExpression();
                            ParentNode.ReplaceChild(ie, replacement);
                            VisitReplacement(replacement);

                            return;
                        }

                        case "ReferenceEquals": {
                            var lhs = ie.Arguments[0];
                            var rhs = ie.Arguments[1];

                            var lhsType = lhs.GetActualType(TypeSystem);
                            var rhsType = rhs.GetActualType(TypeSystem);

                            JSNode replacement;

                            // Structs can never compare equal with ReferenceEquals
                            if (TypeUtil.IsStruct(lhsType) || TypeUtil.IsStruct(rhsType))
                                replacement = JSLiteral.New(false);
                            else
                                replacement = new JSBinaryOperatorExpression(
                                    JSOperator.Equal,
                                    lhs, rhs,
                                    TypeSystem.Boolean
                                );

                            ParentNode.ReplaceChild(ie, replacement);
                            VisitReplacement(replacement);

                            return;
                        }

                        case "GetType": {
                            JSNode replacement;

                            var thisType = JSExpression.DeReferenceType(thisExpression.GetActualType(TypeSystem), false);
                            if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) {
                                var git = (GenericInstanceType)thisType;

                                replacement = new JSTernaryOperatorExpression(
                                    new JSBinaryOperatorExpression(
                                        JSOperator.NotEqual,
                                        thisExpression, new JSNullLiteral(thisType),
                                        TypeSystem.Boolean
                                    ),
                                    new JSTypeOfExpression(git.GenericArguments[0]),
                                    JSIL.ThrowNullReferenceException(),
                                    TypeSystem.SystemType()
                                );
                            } else {
                                replacement = JSIL.GetTypeOf(thisExpression);
                            }

                            ParentNode.ReplaceChild(ie, replacement);
                            VisitReplacement(replacement);

                            return;
                        }
                    }
                } else if (
                    (type != null) &&
                    (type.Type.FullName == "System.ValueType")
                ) {
                    switch (method.Method.Member.Name) {
                        case "Equals": {
                            var replacement = JSIL.StructEquals(ie.ThisReference, ie.Arguments.First());
                            ParentNode.ReplaceChild(ie, replacement);
                            VisitReplacement(replacement);

                            return;
                        }
                    }
                } else if (
                    (type != null) &&
                    IsNullable(type.Type)
                ) {
                    var t = (type.Type as GenericInstanceType).GenericArguments[0];
                    var @null = JSLiteral.Null(t);
                    var @default = new JSDefaultValueLiteral(t);

                    switch (method.Method.Member.Name) {
                        case ".ctor":
                            JSExpression value;
                            if (ie.Arguments.Count == 0) {
                                value = @null;
                            } else {
                                value = ie.Arguments[0];
                            }

                            var boe = new JSBinaryOperatorExpression(
                                JSOperator.Assignment, ie.ThisReference, value, type.Type
                            );
                            ParentNode.ReplaceChild(ie, boe);
                            VisitReplacement(boe);

                            break;

                        case "GetValueOrDefault": {
                            var replacement = JSIL.ValueOfNullableOrDefault(
                                ie.ThisReference,
                                (ie.Arguments.Count == 0)
                                    ? @default
                                    : ie.Arguments[0]
                            );

                            if (ParentNode is JSResultReferenceExpression) {
                                // HACK: Replacing the invocation inside a result reference is incorrect, so we need to walk up the stack
                                //  and replace the result reference with the ternary instead.
                                _ResultReferenceReplacement = replacement;
                            } else {
                                ParentNode.ReplaceChild(ie, replacement);
                                VisitReplacement(replacement);
                            }

                            break;
                        }

                        case "Equals":
                            JSBinaryOperatorExpression equality = new JSBinaryOperatorExpression(JSOperator.Equal, ie.ThisReference, ie.Parameters.First().Value, type.Type);
                            ParentNode.ReplaceChild(ie, equality);
                            VisitReplacement(equality);
                            break;

                        default:
                            throw new NotImplementedException(method.Method.Member.FullName);
                    }

                    return;
                } else if (
                    (type != null) &&
                    TypeUtil.TypesAreEqual(TypeSystem.String, type.Type) &&
                    (method.Method.Name == "Concat")
                ) {
                    if (ie.Arguments.Count > 2) {
                        if (ie.Arguments.All(
                            (arg) => TypeUtil.TypesAreEqual(
                                TypeSystem.String, arg.GetActualType(TypeSystem)
                            )
                        )) {
                            var boe = JSBinaryOperatorExpression.New(
                                JSOperator.Add,
                                ie.Arguments,
                                TypeSystem.String
                            );

                            ParentNode.ReplaceChild(
                                ie,
                                boe
                            );

                            VisitReplacement(boe);
                        }
                    } else if (
                        // HACK: Fix for #239, only convert concat call into + if both sides are non-null literals
                        (ie.Arguments.Count == 2)
                    ) {
                        var lhs = ie.Arguments[0];
                        var rhs = ie.Arguments[1];

                        var isAddOk = (lhs is JSStringLiteral) && (rhs is JSStringLiteral);

                        var lhsType = TypeUtil.DereferenceType(lhs.GetActualType(TypeSystem));
                        if (!(
                            TypeUtil.TypesAreEqual(TypeSystem.String, lhsType) ||
                            TypeUtil.TypesAreEqual(TypeSystem.Char, lhsType)
                        )) {
                            lhs = JSInvocationExpression.InvokeMethod(lhsType, JS.toString, lhs, null);
                            isAddOk = true;
                        }

                        var rhsType = TypeUtil.DereferenceType(rhs.GetActualType(TypeSystem));
                        if (!(
                            TypeUtil.TypesAreEqual(TypeSystem.String, rhsType) ||
                            TypeUtil.TypesAreEqual(TypeSystem.Char, rhsType)
                        )) {
                            rhs = JSInvocationExpression.InvokeMethod(rhsType, JS.toString, rhs, null);
                            isAddOk = true;
                        }

                        if (isAddOk) {
                            var boe = new JSBinaryOperatorExpression(
                                JSOperator.Add, lhs, rhs, TypeSystem.String
                            );

                            ParentNode.ReplaceChild(
                                ie, boe
                            );

                            VisitReplacement(boe);
                        }
                    } else if (
                        TypeUtil.GetTypeDefinition(ie.Arguments[0].GetActualType(TypeSystem)).FullName == "System.Array"
                    ) {
                    } else {
                        var firstArg = ie.Arguments.FirstOrDefault();

                        ParentNode.ReplaceChild(
                            ie, firstArg
                        );

                        if (firstArg != null)
                            VisitReplacement(firstArg);
                    }
                    return;
                } else if (
                    TypeUtil.IsDelegateType(method.Reference.DeclaringType) &&
                    (method.Method.Name == "Invoke")
                ) {
                    var newIe = new JSDelegateInvocationExpression(
                        thisExpression, ie.GetActualType(TypeSystem), ie.Arguments.ToArray()
                    );
                    ParentNode.ReplaceChild(ie, newIe);

                    VisitReplacement(newIe);
                    return;
                } else if (
                    (method.Reference.DeclaringType.Name == "RuntimeHelpers") &&
                    (method.Method.Name == "InitializeArray")
                ) {
                    var array = ie.Arguments[0];
                    var arrayType = array.GetActualType(TypeSystem);
                    var field = ie.Arguments[1].SelfAndChildrenRecursive.OfType<JSField>().First();
                    var initializer = JSArrayExpression.UnpackArrayInitializer(arrayType, field.Field.Member.InitialValue);

                    var copy = JSIL.ShallowCopy(array, initializer, arrayType);
                    ParentNode.ReplaceChild(ie, copy);
                    VisitReplacement(copy);
                    return;
                } else if (
                    method.Reference.DeclaringType.FullName == "System.Reflection.Assembly"
                ) {
                    switch (method.Reference.Name) {
                        case "GetExecutingAssembly": {
                            var assembly = Method.DeclaringType.Module.Assembly;
                            var asmNode = new JSReflectionAssembly(assembly);
                            ParentNode.ReplaceChild(ie, asmNode);
                            VisitReplacement(asmNode);

                            return;
                        }
                    }
                } else if (
                    method.Method.DeclaringType.Definition.FullName == "System.Array" &&
                    (ie.Arguments.Count == 1)
                ) {
                    switch (method.Method.Name) {
                        case "GetLength":
                        case "GetUpperBound": {
                                var index = ie.Arguments[0] as JSLiteral;
                                if (index != null) {
                                    var newDot = JSDotExpression.New(thisExpression, new JSStringIdentifier(
                                        String.Format("length{0}", Convert.ToInt32(index.Literal), true),
                                        TypeSystem.Int32
                                    ));

                                    if (method.Method.Name == "GetUpperBound") {
                                        var newExpr = new JSBinaryOperatorExpression(
                                            JSOperator.Subtract, newDot, JSLiteral.New(1), TypeSystem.Int32
                                        );
                                        ParentNode.ReplaceChild(ie, newExpr);
                                    } else {
                                        ParentNode.ReplaceChild(ie, newDot);
                                    }
                                }
                                break;
                            }
                        case "GetLowerBound":
                            ParentNode.ReplaceChild(ie, JSLiteral.New(0));
                            break;
                    }
                }
            }

            VisitChildren(ie);
        }
        private JSStatement FoldInvocation (JSInvocationExpression invocation, InitializationInfo ii) {
            var arguments = invocation.Arguments.ToArray();
            var newExpression = new JSNewExpression(
                ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments
            );

            // Constructor call contains a reference to the struct being initialized.
            // For some reason the C# compiler lets you do this even though it would be undefined
            //  if not for a nuance in how struct locals work in MSIL.
            if (newExpression.SelfAndChildrenRecursive.Any((n) => n.Equals(invocation.ThisReference)))
                return null;

            ii.Folded = true;
            ii.BinaryExpressionParent.ReplaceChild(ii.ParentBinaryExpression, new JSNullExpression());

            var newBoe = new JSBinaryOperatorExpression(
                JSOperator.Assignment, 
                ii.ParentBinaryExpression.Left, newExpression, 
                ii.ParentBinaryExpression.ActualType
            );

            return new JSVariableDeclarationStatement(newBoe);
        }
Esempio n. 46
0
        public void VisitNode(JSInvocationExpression ie)
        {
            var type = ie.JSType;
            var method = ie.JSMethod;
            var thisExpression = ie.ThisReference;

            if (method != null) {
                if (
                    (type != null) &&
                    (type.Type.FullName == "System.Object")
                ) {
                    switch (method.Method.Member.Name) {
                        case "GetType":
                            JSNode replacement;

                            var thisType = JSExpression.DeReferenceType(thisExpression.GetExpectedType(TypeSystem), false);
                            if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) {
                                replacement = new JSType(thisType);
                            } else {
                                replacement = JSIL.GetType(thisExpression);
                            }

                            ParentNode.ReplaceChild(ie, replacement);
                            VisitReplacement(replacement);

                            return;
                    }
                } else if (
                    (type != null) &&
                    (type.Type.FullName.StartsWith("System.Nullable")) &&
                    (type.Type is GenericInstanceType)
                ) {
                    var t = (type.Type as GenericInstanceType).GenericArguments[0];
                    var @null = JSLiteral.Null(t);
                    var @default = new JSDefaultValueLiteral(t);

                    switch (method.Method.Member.Name) {
                        case ".ctor":
                            JSExpression value;
                            if (ie.Arguments.Count == 0) {
                                value = @null;
                            } else {
                                value = ie.Arguments[0];
                            }

                            var boe = new JSBinaryOperatorExpression(
                                JSOperator.Assignment, ie.ThisReference, value, type.Type
                            );
                            ParentNode.ReplaceChild(ie, boe);
                            VisitReplacement(boe);

                            break;
                        case "GetValueOrDefault":
                            var isNull = new JSBinaryOperatorExpression(
                                JSOperator.Equal, ie.ThisReference, @null, TypeSystem.Boolean
                            );

                            JSTernaryOperatorExpression ternary;
                            if (ie.Arguments.Count == 0) {
                                ternary = new JSTernaryOperatorExpression(
                                    isNull, @default, ie.ThisReference, type.Type
                                );
                            } else {
                                ternary = new JSTernaryOperatorExpression(
                                    isNull, ie.Arguments[0], ie.ThisReference, type.Type
                                );
                            }

                            ParentNode.ReplaceChild(ie, ternary);
                            VisitReplacement(ternary);

                            break;
                        default:
                            throw new NotImplementedException(method.Method.Member.FullName);
                    }

                    return;
                } else if (
                    (type != null) &&
                    ILBlockTranslator.TypesAreEqual(TypeSystem.String, type.Type) &&
                    (method.Method.Name == "Concat")
                ) {
                    if (ie.Arguments.Count > 2) {
                        if (ie.Arguments.All(
                            (arg) => ILBlockTranslator.TypesAreEqual(
                                TypeSystem.String, arg.GetExpectedType(TypeSystem)
                            )
                        )) {
                            var boe = JSBinaryOperatorExpression.New(
                                JSOperator.Add,
                                ie.Arguments,
                                TypeSystem.String
                            );

                            ParentNode.ReplaceChild(
                                ie,
                                boe
                            );

                            VisitReplacement(boe);
                        }
                    } else if (
                        ie.Arguments.Count == 2
                    ) {
                        var lhs = ie.Arguments[0];
                        var lhsType = ILBlockTranslator.DereferenceType(lhs.GetExpectedType(TypeSystem));
                        if (!(
                            ILBlockTranslator.TypesAreEqual(TypeSystem.String, lhsType) ||
                            ILBlockTranslator.TypesAreEqual(TypeSystem.Char, lhsType)
                        )) {
                            lhs = JSInvocationExpression.InvokeMethod(lhsType, JS.toString, lhs, null);
                        }

                        var rhs = ie.Arguments[1];
                        var rhsType = ILBlockTranslator.DereferenceType(rhs.GetExpectedType(TypeSystem));
                        if (!(
                            ILBlockTranslator.TypesAreEqual(TypeSystem.String, rhsType) ||
                            ILBlockTranslator.TypesAreEqual(TypeSystem.Char, rhsType)
                        )) {
                            rhs = JSInvocationExpression.InvokeMethod(rhsType, JS.toString, rhs, null);
                        }

                        var boe = new JSBinaryOperatorExpression(
                            JSOperator.Add, lhs, rhs, TypeSystem.String
                        );

                        ParentNode.ReplaceChild(
                            ie, boe
                        );

                        VisitReplacement(boe);
                    } else if (
                        ILBlockTranslator.GetTypeDefinition(ie.Arguments[0].GetExpectedType(TypeSystem)).FullName == "System.Array"
                    ) {
                    } else {
                        var firstArg = ie.Arguments.FirstOrDefault();

                        ParentNode.ReplaceChild(
                            ie, firstArg
                        );

                        if (firstArg != null)
                            VisitReplacement(firstArg);
                    }
                    return;
                } else if (
                    ILBlockTranslator.IsDelegateType(method.Reference.DeclaringType) &&
                    (method.Method.Name == "Invoke")
                ) {
                    var newIe = new JSDelegateInvocationExpression(
                        thisExpression, ie.GetExpectedType(TypeSystem), ie.Arguments.ToArray()
                    );
                    ParentNode.ReplaceChild(ie, newIe);

                    VisitReplacement(newIe);
                    return;
                } else if (
                    (method.Reference.DeclaringType.FullName == "System.Type") &&
                    (method.Method.Name == "GetTypeFromHandle")
                ) {
                    var typeObj = ie.Arguments.FirstOrDefault();
                    ParentNode.ReplaceChild(ie, typeObj);

                    VisitReplacement(typeObj);
                    return;
                } else if (
                    (method.Reference.DeclaringType.Name == "RuntimeHelpers") &&
                    (method.Method.Name == "InitializeArray")
                ) {
                    var array = ie.Arguments[0];
                    var arrayType = array.GetExpectedType(TypeSystem);
                    var field = ie.Arguments[1].SelfAndChildrenRecursive.OfType<JSField>().First();
                    var initializer = JSArrayExpression.UnpackArrayInitializer(arrayType, field.Field.Member.InitialValue);

                    var copy = JSIL.ShallowCopy(array, initializer, arrayType);
                    ParentNode.ReplaceChild(ie, copy);
                    VisitReplacement(copy);
                    return;
                } else if (
                    method.Method.DeclaringType.Definition.FullName == "System.Array" &&
                    (ie.Arguments.Count == 1)
                ) {
                    switch (method.Method.Name) {
                        case "GetLength":
                        case "GetUpperBound": {
                                var index = ie.Arguments[0] as JSLiteral;
                                if (index != null) {
                                    var newDot = JSDotExpression.New(thisExpression, new JSStringIdentifier(
                                        String.Format("length{0}", Convert.ToInt32(index.Literal)),
                                        TypeSystem.Int32
                                    ));

                                    if (method.Method.Name == "GetUpperBound") {
                                        var newExpr = new JSBinaryOperatorExpression(
                                            JSOperator.Subtract, newDot, JSLiteral.New(1), TypeSystem.Int32
                                        );
                                        ParentNode.ReplaceChild(ie, newExpr);
                                    } else {
                                        ParentNode.ReplaceChild(ie, newDot);
                                    }
                                }
                                break;
                            }
                        case "GetLowerBound":
                            ParentNode.ReplaceChild(ie, JSLiteral.New(0));
                            break;
                    }
                }
            }

            VisitChildren(ie);
        }
        private JSStatement ConvertInvocationIntoReassignment (JSInvocationExpression invocation, InitializationInfo ii) {
            var arguments = invocation.Arguments.ToArray();
            var newExpression = new JSNewExpression(
                ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments
            );
            var newBoe = new JSBinaryOperatorExpression(
                JSOperator.Assignment, 
                ii.ParentBinaryExpression.Left, newExpression, 
                ii.ParentBinaryExpression.ActualType
            );

            return new JSExpressionStatement(newBoe);
        }