public void VisitNode(JSContinueExpression cont) { var parentLoop = Stack.OfType <JSLoopStatement>().FirstOrDefault(); if ( (parentLoop != null) && (cont.TargetLoop == parentLoop.Index) ) { if ( Stack.Skip(1).TakeWhile((s) => s != parentLoop) .All((s) => { var l = s.Children.LastOrDefault(); if (l != null) { return(l.SelfAndChildrenRecursive.Contains(cont)); } else { return(false); } }) ) { var newNull = new JSNullExpression(); ParentNode.ReplaceChild(cont, newNull); VisitReplacement(newNull); return; } } VisitChildren(cont); }
protected void VisitControlFlowNode(JSNode node) { var stackSlice = Stack.Take(3).ToArray(); var parentEs = stackSlice[1] as JSExpressionStatement; var parentBlock = stackSlice[2] as JSBlockStatement; if ((parentEs != null) && (parentBlock == BlockStack.Peek())) { AbsoluteJumpsSeen += 1; if (AbsoluteJumpsSeen > 1) { if (TraceLevel >= 1) { Console.WriteLine("// Eliminating {0}", node); } var replacement = new JSNullExpression(); ParentNode.ReplaceChild(node, replacement); MadeChanges = true; return; } else { if (TraceLevel >= 4) { Console.WriteLine("// Not eliminating {0}", node); } } } VisitChildren(node); }
public void VisitNode(JSContinueExpression cont) { var parentLoop = Stack.OfType<JSLoopStatement>().FirstOrDefault(); if ( (parentLoop != null) && (cont.TargetLoop == parentLoop.Index) ) { if ( Stack.Skip(1).TakeWhile((s) => s != parentLoop) .All((s) => { var l = s.Children.LastOrDefault(); if (l != null) return l.SelfAndChildrenRecursive.Contains(cont); else return false; }) ) { var newNull = new JSNullExpression(); ParentNode.ReplaceChild(cont, newNull); VisitReplacement(newNull); return; } } VisitChildren(cont); }
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); }
public void VisitNode(JSGotoExpression ge) { var stackSlice = Stack.Take(3).ToArray(); var parentEs = stackSlice[1] as JSExpressionStatement; var parentBlock = stackSlice[2] as JSBlockStatement; if ((parentEs != null) && (parentBlock == BlockStack.Peek())) { AbsoluteJumpsSeen += 1; if (AbsoluteJumpsSeen > 1) { if (TraceLevel >= 1) Console.WriteLine("// Eliminating {0}", ge); var replacement = new JSNullExpression(); ParentNode.ReplaceChild(ge, replacement); return; } else { if (TraceLevel >= 3) Console.WriteLine("// Not eliminating {0}", ge); } } VisitChildren(ge); }
protected JSExpression Translate_Call(ILExpression node, MethodReference method) { var methodInfo = TypeInfo.GetMethod(method); if (methodInfo == null) return new JSIgnoredMemberReference(true, null, JSLiteral.New(method.FullName)); var thisType = DereferenceType(ThisMethod.DeclaringType); var declaringType = DereferenceType(method.DeclaringType); var declaringTypeDef = GetTypeDefinition(declaringType); var arguments = Translate(node.Arguments, method.Parameters, method.HasThis); JSExpression thisExpression; bool explicitThis = false; if (method.HasThis) { var firstArg = node.Arguments.First(); var ilv = firstArg.Operand as ILVariable; var firstArgType = DereferenceType(firstArg.ExpectedType); if (IsInvalidThisExpression(firstArg)) { if (!JSReferenceExpression.TryDereference(JSIL, arguments[0], out thisExpression)) { if (arguments[0].IsNull) thisExpression = arguments[0]; else throw new InvalidOperationException("this-expression for method invocation on value type must be a reference"); } } else { thisExpression = arguments[0]; } arguments = arguments.Skip(1).ToArray(); // If the call is of the form x.Method(...), we don't need to specify the this parameter // explicitly using the form type.Method.call(x, ...). // Make sure that 'this' references only pass this check when they don't refer to // members of base types. It's always okay to use thiscall form for interfaces, since we qualify // the name of the method/property. if ( ((TypesAreEqual(declaringType, firstArgType)) && ( (ilv == null) || (ilv.Name != "this") || (TypesAreEqual(thisType, firstArgType)) )) || ( (declaringTypeDef != null) && (declaringTypeDef.IsInterface) && TypesAreAssignable(declaringTypeDef, thisType) && TypesAreAssignable(declaringTypeDef, firstArgType) ) ) { } else { explicitThis = true; } } else { explicitThis = true; thisExpression = new JSNullExpression(); } var result = Translate_MethodReplacement( new JSMethod(method, methodInfo), thisExpression, arguments, false, !method.HasThis, explicitThis ); if (method.ReturnType.MetadataType != MetadataType.Void) { var expectedType = node.ExpectedType ?? node.InferredType ?? method.ReturnType; if (!TypesAreAssignable(expectedType, result.GetExpectedType(TypeSystem))) result = Translate_Conv(result, expectedType); } if (CopyOnReturn(result.GetExpectedType(TypeSystem))) result = JSReferenceExpression.New(result); return result; }
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 "GetType": { JSNode replacement; var thisType = JSExpression.DeReferenceType(thisExpression.GetActualType(TypeSystem), false); if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) { replacement = new JSType(thisType); } else { replacement = JSIL.GetTypeOf(thisExpression); } ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } } else if ( 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 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) && 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 ( ie.Arguments.Count == 2 ) { var lhs = ie.Arguments[0]; 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); } var rhs = ie.Arguments[1]; 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); } 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.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.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 JSAssembly(assembly); ParentNode.ReplaceChild(ie, asmNode); VisitReplacement(asmNode); return; } case "GetType": { switch (method.Method.Parameters.Length) { case 1: case 2: JSExpression throwOnFail = new JSBooleanLiteral(false); if (method.Method.Parameters.Length == 2) throwOnFail = ie.Arguments[1]; var invocation = JSIL.GetTypeFromAssembly( ie.ThisReference, ie.Arguments[0], throwOnFail ); ParentNode.ReplaceChild(ie, invocation); VisitReplacement(invocation); return; } break; } } } 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); }
protected JSExpression Translate_Call(ILExpression node, MethodReference method) { var methodInfo = TypeInfo.GetMethod(method); if (methodInfo == null) return new JSIgnoredMemberReference(true, null, JSLiteral.New(method.FullName)); var declaringType = TypeUtil.DereferenceType(method.DeclaringType); var declaringTypeDef = TypeUtil.GetTypeDefinition(declaringType); var declaringTypeInfo = TypeInfo.Get(declaringType); var arguments = Translate(node.Arguments, method.Parameters, method.HasThis); JSExpression thisExpression; bool explicitThis = false; if (method.HasThis) { var firstArg = node.Arguments.First(); var ilv = firstArg.Operand as ILVariable; var firstArgType = TypeUtil.DereferenceType(firstArg.ExpectedType); if (IsInvalidThisExpression(firstArg)) { if (!JSReferenceExpression.TryDereference(JSIL, arguments[0], out thisExpression)) { if (arguments[0].IsNull) thisExpression = arguments[0]; else throw new InvalidOperationException(String.Format( "The method '{0}' was invoked on a value type, but the this-reference was not a reference: {1}", method, node.Arguments[0] )); } } else { thisExpression = arguments[0]; } arguments = arguments.Skip(1).ToArray(); var thisReferenceType = thisExpression.GetActualType(TypeSystem); var isSelf = TypeUtil.TypesAreAssignable( TypeInfo, thisReferenceType, ThisMethod.DeclaringType ); explicitThis = NeedsExplicitThis( declaringType, declaringTypeDef, declaringTypeInfo, isSelf, thisReferenceType, methodInfo ); } else { explicitThis = true; thisExpression = new JSNullExpression(); } var result = Translate_MethodReplacement( new JSMethod(method, methodInfo, MethodTypes), thisExpression, arguments, false, !method.HasThis, explicitThis || methodInfo.IsConstructor ); if (method.ReturnType.MetadataType != MetadataType.Void) { var expectedType = node.ExpectedType ?? node.InferredType ?? method.ReturnType; if (!TypeUtil.TypesAreAssignable(TypeInfo, expectedType, result.GetActualType(TypeSystem))) result = Translate_Conv(result, expectedType); } if (CopyOnReturn(result.GetActualType(TypeSystem))) result = JSReferenceExpression.New(result); return result; }
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(), new TypeReference("System", "Type", TypeSystem.Object.Module, TypeSystem.Object.Scope, false) ); } 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 "get_HasValue": { var replacement = JSIL.NullableHasValue(ie.ThisReference); if (ParentNode is JSResultReferenceExpression) { _ResultReferenceReplacement = replacement; } else { ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); } break; } case "get_Value": { var replacement = JSIL.ValueOfNullable(ie.ThisReference); if (ParentNode is JSResultReferenceExpression) { _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)), 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); }
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 "GetType": { JSNode replacement; var thisType = JSExpression.DeReferenceType(thisExpression.GetActualType(TypeSystem), false); if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) { replacement = new JSType(thisType); } else { replacement = JSIL.GetTypeOf(thisExpression); } ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } } else if ( 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 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) && 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 ( ie.Arguments.Count == 2 ) { var lhs = ie.Arguments[0]; 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); } var rhs = ie.Arguments[1]; 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); } 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.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.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 JSAssembly(assembly); ParentNode.ReplaceChild(ie, asmNode); VisitReplacement(asmNode); return; } case "GetType": { switch (method.Method.Parameters.Length) { case 1: case 2: JSExpression throwOnFail = new JSBooleanLiteral(false); if (method.Method.Parameters.Length == 2) { throwOnFail = ie.Arguments[1]; } var invocation = JSIL.GetTypeFromAssembly( ie.ThisReference, ie.Arguments[0], throwOnFail ); ParentNode.ReplaceChild(ie, invocation); VisitReplacement(invocation); return; } break; } } } 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); }