public void VisitNode (JSDefaultValueLiteral dvl) { var parentBoe = ParentNode as JSBinaryOperatorExpression; if (CurrentName != "DefaultValue") { var thisReferenceType = dvl.GetActualType(TypeSystem); if (TypeUtil.IsStruct(thisReferenceType) && (parentBoe != null)) Initializations.Add(new InitializationInfo { Type = thisReferenceType, DefaultValueLiteral = dvl, ParentBinaryExpression = parentBoe, BinaryExpressionParent = Stack.Skip(2).First() }); } VisitChildren(dvl); }
public void VisitNode (JSDefaultValueLiteral dvl) { var literalType = dvl.GetActualType(TypeSystem); if (IsLongOrULong(literalType)) { JSNode replacement; if (literalType.MetadataType == MetadataType.Int64) replacement = GetLongLiteralExpression(0); else replacement = GetLongLiteralExpression(0, unsigned: true); ParentNode.ReplaceChild(dvl, replacement); VisitReplacement(replacement); return; } VisitChildren(dvl); }
public void VisitNode(JSDefaultValueLiteral dvl) { var expectedType = dvl.GetActualType(TypeSystem); if ( IsNullable(expectedType) ) { ParentNode.ReplaceChild( dvl, JSLiteral.Null(expectedType) ); } else { VisitChildren(dvl); } }
public void VisitNode(JSDefaultValueLiteral dvl) { var parentBoe = ParentNode as JSBinaryOperatorExpression; if (CurrentName != "DefaultValue") { var thisReferenceType = dvl.GetActualType(TypeSystem); if (TypeUtil.IsStruct(thisReferenceType) && (parentBoe != null)) { Initializations.Add(new InitializationInfo { Type = thisReferenceType, DefaultValueLiteral = dvl, ParentBinaryExpression = parentBoe, BinaryExpressionParent = Stack.Skip(2).First() }); } } VisitChildren(dvl); }
public void VisitNode(JSDefaultValueLiteral dvl) { var literalType = dvl.GetActualType(TypeSystem); if (IsLongOrULong(literalType)) { JSNode replacement; if (literalType.MetadataType == MetadataType.Int64) { replacement = GetLongLiteralExpression(0); } else { replacement = GetLongLiteralExpression(0, unsigned: true); } ParentNode.ReplaceChild(dvl, replacement); VisitReplacement(replacement); return; } VisitChildren(dvl); }
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); }
public void VisitNode(JSDefaultValueLiteral dvl) { var expectedType = dvl.GetExpectedType(TypeSystem); if ( (expectedType != null) && expectedType.FullName.StartsWith("System.Nullable") ) { ParentNode.ReplaceChild( dvl, JSLiteral.Null(expectedType) ); } else { VisitChildren(dvl); } }
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 (JSDefaultValueLiteral dvl) { var expectedType = dvl.GetActualType(TypeSystem); if ( IsNullable(expectedType) ) { ParentNode.ReplaceChild( dvl, JSLiteral.Null(expectedType) ); } else { VisitChildren(dvl); } }
protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex, JSBlockStatement enclosingBlock) { var oldDeclaration = statement.Declarations[declarationIndex]; var valueType = oldDeclaration.Right.GetActualType(JSIL.TypeSystem); var newVariable = variable.Reference(); var enclosingFunction = Stack.OfType<JSFunctionExpression>().First(); JSExpression initialValue; // If the declaration was in function scope originally we can hoist the initial value // into our new variable declaration. If not, we need to initialize the ref variable // to the default value for its type. It will get the correct value assigned later. if (enclosingBlock == enclosingFunction.Body) initialValue = oldDeclaration.Right; else initialValue = new JSDefaultValueLiteral(valueType); var newDeclaration = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, // We have to use a constructed ref to the variable here, otherwise // the declaration will look like 'var x.value = foo' new JSVariable(variable.Identifier, variable.IdentifierType, variable.Function), JSIL.NewReference(initialValue), newVariable.IdentifierType )); if (Tracing) Debug.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement)); // Insert the new declaration directly before the top-level block containing the original // declaration. This ensures that if its initial value has a dependency on external state, // the declaration will not precede the values it depends on. // Note that for declarations that were hoisted out of inner blocks (conditionals, loops) // it doesn't actually matter where the insert occurs, since we initialize with a default // value in that case. enclosingFunction.Body.InsertNearChildRecursive( statement, newDeclaration, 0 ); // If the reference is being declared in function scope, it doesn't need a separate assignment // for its initialization. Otherwise, we need to insert an assignment after the original variable // declaration statement to ensure that the reference variable is initialized to the right value // at the exact right point in the function's execution. if (enclosingBlock != enclosingFunction.Body) { var newAssignment = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, newVariable, oldDeclaration.Right, valueType ) ); var insertLocation = enclosingBlock.Statements.IndexOf(statement) + 1; enclosingBlock.Statements.Insert(insertLocation, newAssignment); } Variables[variable.Identifier] = newVariable; statement.Declarations.RemoveAt(declarationIndex); TransformedVariables.Add(variable.Identifier); }
protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex, JSBlockStatement enclosingBlock) { var oldDeclaration = statement.Declarations[declarationIndex]; var valueType = oldDeclaration.Right.GetActualType(JSIL.TypeSystem); var newVariable = variable.Reference(); var enclosingFunction = Stack.OfType <JSFunctionExpression>().First(); JSExpression initialValue; // If the declaration was in function scope originally we can hoist the initial value // into our new variable declaration. If not, we need to initialize the ref variable // to the default value for its type. It will get the correct value assigned later. if (enclosingBlock == enclosingFunction.Body) { initialValue = oldDeclaration.Right; } else { initialValue = new JSDefaultValueLiteral(valueType); } var newDeclaration = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, // We have to use a constructed ref to the variable here, otherwise // the declaration will look like 'var x.value = foo' new JSVariable(variable.Identifier, variable.IdentifierType, variable.Function), JSIL.NewReference(initialValue), newVariable.IdentifierType )); if (Tracing) { Console.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement)); } // Insert the new declaration directly before the top-level block containing the original // declaration. This ensures that if its initial value has a dependency on external state, // the declaration will not precede the values it depends on. // Note that for declarations that were hoisted out of inner blocks (conditionals, loops) // it doesn't actually matter where the insert occurs, since we initialize with a default // value in that case. enclosingFunction.Body.InsertNearChildRecursive( statement, newDeclaration, 0 ); // If the reference is being declared in function scope, it doesn't need a separate assignment // for its initialization. Otherwise, we need to insert an assignment after the original variable // declaration statement to ensure that the reference variable is initialized to the right value // at the exact right point in the function's execution. if (enclosingBlock != enclosingFunction.Body) { var newAssignment = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, newVariable, oldDeclaration.Right, valueType ) ); var insertLocation = enclosingBlock.Statements.IndexOf(statement) + 1; enclosingBlock.Statements.Insert(insertLocation, newAssignment); } Variables[variable.Identifier] = newVariable; statement.Declarations.RemoveAt(declarationIndex); TransformedVariables.Add(variable.Identifier); }
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(JSCastExpression ce) { var currentType = ce.Expression.GetActualType(TypeSystem); var targetType = ce.NewType; JSExpression newExpression = null; if (targetType.FullName == "System.ValueType") { var replacement = ce.Expression; ParentNode.ReplaceChild(ce, replacement); VisitReplacement(replacement); return; } else if (targetType.MetadataType == MetadataType.Char) { newExpression = JSInvocationExpression.InvokeStatic( JS.fromCharCode, new[] { ce.Expression }, true ); } else if ( (currentType.MetadataType == MetadataType.Char) && TypeUtil.IsIntegral(targetType) ) { newExpression = JSInvocationExpression.InvokeMethod( JS.charCodeAt, ce.Expression, new[] { JSLiteral.New(0) }, true ); } else if ( IntroduceEnumCasts.IsEnumOrNullableEnum(currentType) ) { var enumInfo = TypeInfo.Get(currentType); var isNullable = TypeUtil.IsNullable(currentType); if (targetType.MetadataType == MetadataType.Boolean) { EnumMemberInfo enumMember; if (enumInfo.ValueToEnumMember.TryGetValue(0, out enumMember)) { newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, ce.Expression, new JSEnumLiteral(enumMember.Value, enumMember), TypeSystem.Boolean ); } else if (enumInfo.ValueToEnumMember.TryGetValue(1, out enumMember)) { newExpression = new JSBinaryOperatorExpression( JSOperator.Equal, ce.Expression, new JSEnumLiteral(enumMember.Value, enumMember), TypeSystem.Boolean ); } else { newExpression = new JSUntranslatableExpression(String.Format( "Could not cast enum of type '{0}' to boolean because it has no zero value or one value", currentType.FullName )); } } else if (TypeUtil.IsNumeric(targetType)) { if (isNullable) { newExpression = JSIL.ValueOfNullable( ce.Expression ); } else { newExpression = JSInvocationExpression.InvokeMethod( JS.valueOf(targetType), ce.Expression, null, true ); } } else if (targetType.FullName == "System.Enum") { newExpression = ce.Expression; } else { // Debugger.Break(); } } else if ( (targetType.MetadataType == MetadataType.Boolean) && // A cast from Object to Boolean can occur in two forms: // An implied conversion, where an object expression is treated as a boolean (logicnot operation, etc). // In this case, we want to do 'obj != null' to make it a boolean. // An explicit conversion, where an object expression is unboxed to boolean. // In this case we want to leave it as-is. (ce.IsCoercion || (currentType.FullName != "System.Object")) ) { JSLiteral nullLiteral; // HACK: Necessary because we represent Char instances as JS strings. Issue #150 // Is this right? if (currentType.FullName == "System.Char") nullLiteral = new JSDefaultValueLiteral(currentType); else nullLiteral = new JSNullLiteral(currentType); newExpression = new JSBinaryOperatorExpression( JSBinaryOperator.NotEqual, ce.Expression, nullLiteral, TypeSystem.Boolean ); } else if ( TypeUtil.IsNumeric(targetType) && TypeUtil.IsNumeric(currentType) && !TypeUtil.TypesAreEqual(targetType, currentType, true) ) { if (currentType.MetadataType == MetadataType.Int64) { if (targetType.MetadataType == MetadataType.UInt64) { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToUInt64", TypeSystem.Int32, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } else { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToNumber", TypeSystem.Int32, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } } else if (currentType.MetadataType == MetadataType.UInt64) { if (targetType.MetadataType == MetadataType.Int64) { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToInt64", TypeSystem.Int32, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } else { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToNumber", TypeSystem.Int32, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } } else if (targetType.MetadataType == MetadataType.Int64) { newExpression = JSInvocationExpression.InvokeStatic( new JSType(TypeSystem.Int64), new JSFakeMethod("FromNumber", TypeSystem.Int64, new[] { currentType }, MethodTypeFactory), new[] { ce.Expression }, true); } else if (targetType.MetadataType == MetadataType.UInt64) { newExpression = JSInvocationExpression.InvokeStatic( new JSType(TypeSystem.UInt64), new JSFakeMethod("FromNumber", TypeSystem.UInt64, new[] { currentType }, MethodTypeFactory), new[] { ce.Expression }, true); } else if ( TypeUtil.IsIntegral(currentType) || !TypeUtil.IsIntegral(targetType)) { newExpression = ce.Expression; } else { newExpression = new JSTruncateExpression(ce.Expression); } } else { // newExpression = JSIL.Cast(ce.Expression, targetType); } if (newExpression != null) { ParentNode.ReplaceChild(ce, newExpression); VisitReplacement(newExpression); } else { // Debugger.Break(); VisitChildren(ce); } }
public void VisitNode(JSDefaultValueLiteral dvl) { // We need to check this since it's a literal and it could show up in the tree multiple times. if (!dvl.CachedTypeIndex.HasValue) { var ct = GetCachedType(dvl.Value); if (ct != null) dvl.CachedTypeIndex = ct.Index; } VisitChildren(dvl); }
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); }
public void VisitNode(JSDefaultValueLiteral dvl) { var ct = GetCachedType(dvl.Value); if (ct != null) dvl.CachedTypeIndex = ct.Index; VisitChildren(dvl); }