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); }
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() )); }
protected JSInvocationExpression CastToInteger(JSExpression booleanExpression) { return(JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.SByte), booleanExpression, null, true )); }
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); }
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); }
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); } }
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)); }
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; } var thisArgumentKnownType = JSType.ExtractType(thisArgument); if (thisArgumentKnownType != null) { var replacement = translator.DoJSILMethodReplacement( thisArgumentKnownType.FullName, memberName, null, // FIXME null, argumentValues ); if (replacement != null) { return(replacement); } } return(JSInvocationExpression.InvokeMethod( new JSFakeMethod( memberName, returnType, (from av in argumentValues select av.GetActualType(translator.TypeSystem)).ToArray(), translator.MethodTypes, TypeArguments, escape: true ), thisArgument, arguments.Skip(2).ToArray() )); }
public void VisitNode(JSIndexerExpression ie) { var indexType = ie.Index.GetActualType(TypeSystem); if ( !TypeUtil.IsIntegral(indexType) && IsEnumOrNullableEnum(indexType) ) { var cast = JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.Int32), ie.Index, null, true ); ie.ReplaceChild(ie.Index, cast); } VisitChildren(ie); }
public void VisitNode(JSSwitchStatement ss) { var conditionType = ss.Condition.GetActualType(TypeSystem); if ( !TypeUtil.IsIntegral(conditionType) && IsEnumOrNullableEnum(conditionType) ) { var cast = JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.Int32), ss.Condition, null, true ); ss.ReplaceChild(ss.Condition, cast); } VisitChildren(ss); }
public void VisitNode(JSUnaryOperatorExpression uoe) { var type = uoe.Expression.GetActualType(TypeSystem); var isEnum = IsEnumOrNullableEnum(type); if (isEnum) { var castToInt = JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.Int32), uoe.Expression, null, true ); if (LogicalOperators.Contains(uoe.Operator)) { uoe.ReplaceChild(uoe.Expression, castToInt); } else if (uoe.Operator == JSOperator.Negation) { uoe.ReplaceChild(uoe.Expression, castToInt); } else if (uoe.Operator is JSUnaryMutationOperator) { if ( (uoe.Operator == JSOperator.PreIncrement) || (uoe.Operator == JSOperator.PreDecrement) ) { var assignment = MakeUnaryMutation( uoe.Expression, (uoe.Operator == JSOperator.PreDecrement) ? JSOperator.Subtract : JSOperator.Add, type ); ParentNode.ReplaceChild(uoe, assignment); VisitReplacement(assignment); return; } else if ( (uoe.Operator == JSOperator.PostIncrement) || (uoe.Operator == JSOperator.PostDecrement) ) { // FIXME: Terrible hack var tempVariable = TemporaryVariable.ForFunction( Stack.Last() as JSFunctionExpression, type ); var makeTempCopy = new JSBinaryOperatorExpression( JSOperator.Assignment, tempVariable, uoe.Expression, type ); var assignment = MakeUnaryMutation( uoe.Expression, (uoe.Operator == JSOperator.PostDecrement) ? JSOperator.Subtract : JSOperator.Add, type ); var comma = new JSCommaExpression( makeTempCopy, assignment, tempVariable ); ParentNode.ReplaceChild(uoe, comma); VisitReplacement(comma); return; } else { throw new NotImplementedException("Unary mutation of enum not supported: " + uoe.ToString()); } } } VisitChildren(uoe); }
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(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); var eitherIsEnum = leftIsEnum || rightIsEnum; var assignmentOperator = boe.Operator as JSAssignmentOperator; JSBinaryOperator replacementOperator; if (LogicalOperators.Contains(boe.Operator)) { if (eitherIsEnum) { if (leftIsEnum) { var cast = JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.Int32), boe.Left, null, true ); boe.ReplaceChild(boe.Left, cast); } if (rightIsEnum) { var cast = JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.Int32), boe.Right, null, true ); boe.ReplaceChild(boe.Right, cast); } } } else if (BitwiseOperators.Contains(boe.Operator)) { var parentCast = ParentNode as JSCastExpression; if (resultIsEnum && ((parentCast == null) || (parentCast.NewType != resultType))) { var cast = JSCastExpression.New( boe, resultType, TypeSystem, true ); ParentNode.ReplaceChild(boe, cast); VisitReplacement(cast); } } else if ( (assignmentOperator != null) && ReverseCompoundAssignments.TryGetValue(assignmentOperator, out replacementOperator) && leftIsEnum ) { var replacement = new JSBinaryOperatorExpression( JSOperator.Assignment, boe.Left, JSCastExpression.New( new JSBinaryOperatorExpression( replacementOperator, boe.Left, boe.Right, TypeSystem.Int32 ), leftType, TypeSystem, true ), leftType ); ParentNode.ReplaceChild(boe, replacement); VisitReplacement(replacement); return; } VisitChildren(boe); }
public void VisitNode(JSCastExpression ce) { var currentType = ce.Expression.GetActualType(TypeSystem); var targetType = ce.NewType; JSExpression newExpression = null; 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); 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)) { newExpression = JSInvocationExpression.InvokeStatic( JS.Number(targetType), new[] { ce.Expression }, true ); } else if (targetType.FullName == "System.Enum") { newExpression = ce.Expression; } else { // Debugger.Break(); } } else if ( targetType.MetadataType == MetadataType.Boolean ) { newExpression = new JSBinaryOperatorExpression( JSBinaryOperator.NotEqual, ce.Expression, new JSDefaultValueLiteral(currentType), TypeSystem.Boolean ); } else if ( TypeUtil.IsNumeric(targetType) && TypeUtil.IsNumeric(currentType) ) { if ( TypeUtil.IsIntegral(currentType) || !TypeUtil.IsIntegral(targetType) ) { newExpression = ce.Expression; } else { newExpression = JSInvocationExpression.InvokeStatic(JS.floor, new[] { ce.Expression }, true); } } else { newExpression = JSIL.Cast(ce.Expression, targetType); } if (newExpression != null) { ParentNode.ReplaceChild(ce, newExpression); VisitReplacement(newExpression); } else { // Debugger.Break(); VisitChildren(ce); } }
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(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); var eitherIsEnum = leftIsEnum || rightIsEnum; var assignmentOperator = boe.Operator as JSAssignmentOperator; JSBinaryOperator replacementOperator; JSBinaryOperatorExpression replacement; if (LogicalOperators.Contains(boe.Operator)) { if (eitherIsEnum) { if (leftIsEnum) { var cast = JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.Int32), boe.Left, null, true ); boe.ReplaceChild(boe.Left, cast); } if (rightIsEnum) { var cast = JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.Int32), boe.Right, null, true ); boe.ReplaceChild(boe.Right, cast); } } } else if (BitwiseOperators.Contains(boe.Operator)) { var parentCast = ParentNode as JSCastExpression; var parentReinterpret = Stack.Skip(2).FirstOrDefault() as JSChangeTypeExpression; if (resultIsEnum && ((parentCast == null) || (parentCast.NewType != resultType)) && ((parentReinterpret == null) || (parentReinterpret.NewType != resultType)) ) { var cast = CastToEnumType(boe, resultType); ParentNode.ReplaceChild(boe, cast); VisitReplacement(cast); } } else if ( leftIsEnum && ((replacement = DeconstructMutationAssignment(boe, TypeSystem, TypeSystem.Int32)) != null) ) { ParentNode.ReplaceChild(boe, replacement); VisitReplacement(replacement); return; } VisitChildren(boe); }
private JSExpression ConstructInvocation( JSPropertyAccess pa, JSExpression argument = null ) { JSExpression[] arguments; if (argument == null) { arguments = new JSExpression[0]; } else { arguments = new JSExpression[] { argument } }; var originalMethod = pa.OriginalMethod; var declaringType = originalMethod.Reference.DeclaringType; var declaringTypeDef = TypeUtil.GetTypeDefinition(originalMethod.Reference.DeclaringType); var thisReferenceType = pa.ThisReference.GetActualType(TypeSystem); var isSelf = TypeUtil.TypesAreAssignable( TypeInfo, thisReferenceType, declaringType ); // ILSpy converts compound assignments from: // x.set_Value(x.get_Value + n) // to // x.get_Value += n; // so we have to detect this and reconstruct the correct method // references. var actualMethod = originalMethod; var correctName = String.Format( "{0}_{1}", pa.IsWrite ? "set" : "get", pa.Property.Property.ShortName ); if (!actualMethod.Reference.Name.Contains(correctName)) { var dt = originalMethod.Method.DeclaringType; var actualMethodInfo = dt.Members.Values .OfType <MethodInfo>().FirstOrDefault( (m) => ( m.Name.Contains(correctName) && m.DeclaringType == originalMethod.Method.DeclaringType ) ); MethodReference actualMethodReference = actualMethodInfo.Member; if (originalMethod.Reference is GenericInstanceMethod) { throw new InvalidDataException("Reconstructing an invocation of a generic instance method? Shouldn't be possible."); } else if (declaringType is GenericInstanceType) { var declaringGit = (GenericInstanceType)declaringType; var returnType = actualMethodReference.ReturnType; if (TypeUtil.IsOpenType(returnType)) { var actualReturnType = JSExpression.SubstituteTypeArgs(TypeInfo, actualMethodReference.ReturnType, actualMethodReference); returnType = actualReturnType; } actualMethodReference = new MethodReference( actualMethodReference.Name, returnType, declaringGit ); } actualMethod = new JSMethod( actualMethodReference, actualMethodInfo, originalMethod.MethodTypes, originalMethod.GenericArguments ); } bool needsExplicitThis = !pa.IsVirtualCall && ILBlockTranslator.NeedsExplicitThis( declaringType, declaringTypeDef, originalMethod.Method.DeclaringType, isSelf, thisReferenceType, originalMethod.Method ); JSInvocationExpressionBase invocation; if (pa.Property.Property.IsStatic) { invocation = JSInvocationExpression.InvokeStatic( actualMethod.Reference.DeclaringType, actualMethod, arguments ); } else if (needsExplicitThis) { invocation = JSInvocationExpression.InvokeBaseMethod( actualMethod.Reference.DeclaringType, actualMethod, pa.ThisReference, arguments ); } else { invocation = JSInvocationExpression.InvokeMethod( pa.OriginalType, actualMethod, pa.ThisReference, arguments ); } JSExpression replacement; if (TypeUtil.IsStruct(pa.Property.Property.ReturnType)) { replacement = new JSResultReferenceExpression(invocation); } else { replacement = invocation; } return(replacement); }
public void VisitNode(JSCastExpression ce) { var currentType = ce.Expression.GetActualType(TypeSystem); var targetType = ce.NewType; JSExpression newExpression = null; var innerCast = ce.Expression as JSCastExpression; if (targetType.FullName == "System.ValueType") { var replacement = ce.Expression; ParentNode.ReplaceChild(ce, replacement); VisitReplacement(replacement); return; } else if ( TypeUtil.IsIntegralOrEnum(currentType) && (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) ) { TypeInfo enumInfo; var isNullable = TypeUtil.IsNullable(currentType); if (isNullable) { int temp; var git = (GenericInstanceType)TypeUtil.FullyDereferenceType(currentType, out temp); enumInfo = TypeInfo.Get(git.GenericArguments[0]); } else { enumInfo = TypeInfo.Get(currentType); } if (enumInfo == null) { throw new InvalidOperationException("Unable to extract enum type from typereference " + currentType); } if (targetType.MetadataType == MetadataType.Boolean) { newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, JSCastExpression.New(ce.Expression, TypeSystem.Int32, TypeSystem, true, true), new JSIntegerLiteral(0, typeof(Int32)), TypeSystem.Boolean ); } else if (TypeUtil.IsNumeric(targetType)) { if (isNullable) { newExpression = new JSNullableCastExpression(ce.Expression, new JSType(targetType)); } else if ( ce.Expression is JSCastExpression && (((JSCastExpression)ce.Expression).Expression.GetActualType(TypeSystem).MetadataType == MetadataType.Int64 || ((JSCastExpression)ce.Expression).Expression.GetActualType(TypeSystem).MetadataType == MetadataType.UInt64) ) { newExpression = 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) && (ce.Expression is JSAsExpression) && ((JSAsExpression)ce.Expression).GetActualType(TypeSystem) is GenericParameter ) { // C# expressions such as (t is T) (where T is a generic parameter). See issue #150. // Tested with AsWithGenericParameter.cs newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, ce.Expression, new JSNullLiteral(currentType), TypeSystem.Boolean ); } 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")) ) { newExpression = new JSBinaryOperatorExpression( JSOperator.NotEqual, ce.Expression, new JSDefaultValueLiteral(currentType), TypeSystem.Boolean ); } else if ( TypeUtil.IsNumeric(targetType) && TypeUtil.IsNumeric(currentType) && !TypeUtil.TypesAreEqual(targetType, currentType, true) ) { TypeReference innerType = null; if (innerCast != null) { innerType = innerCast.Expression.GetActualType(TypeSystem); } if ( TypeUtil.TypesAreAssignable(TypeInfo, targetType, innerType) && ( (TypeUtil.IsFloatingPoint(targetType) == TypeUtil.IsFloatingPoint(currentType)) && (TypeUtil.IsFloatingPoint(targetType) == TypeUtil.IsFloatingPoint(innerType)) ) && (TypeUtil.IsSigned(targetType) == TypeUtil.IsSigned(innerType)) && (TypeUtil.SizeOfType(targetType) <= TypeUtil.SizeOfType(currentType)) && (TypeUtil.SizeOfType(targetType) >= TypeUtil.SizeOfType(innerType)) ) { // HACK: Turn pointless conversions like '(int32)(int64)(1 + 2)' into '(1 + 2)' newExpression = innerCast.Expression; } else if (currentType.MetadataType == MetadataType.Int64) { if (!EmulateInt64) { newExpression = ce; } else if (targetType.MetadataType == MetadataType.UInt64) { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToUInt64", TypeSystem.UInt64, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } else { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToNumber", targetType, new TypeReference[] { TypeSystem.Double, TypeSystem.Boolean }, MethodTypeFactory), ce.Expression, GetInt64ConversionArgs(targetType)); } } else if (currentType.MetadataType == MetadataType.UInt64) { if (!EmulateInt64) { newExpression = ce; } else if (targetType.MetadataType == MetadataType.Int64) { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToInt64", TypeSystem.Int64, new TypeReference[] { }, MethodTypeFactory), ce.Expression); } else { newExpression = JSInvocationExpression .InvokeMethod( TypeSystem.Int64, new JSFakeMethod("ToNumber", targetType, new TypeReference[] { TypeSystem.Double, TypeSystem.Boolean }, MethodTypeFactory), ce.Expression, GetInt64ConversionArgs(targetType)); } } else if (targetType.MetadataType == MetadataType.Int64) { if (!EmulateInt64) { newExpression = ce; } else { 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) { if (!EmulateInt64) { newExpression = ce; } else { newExpression = JSInvocationExpression.InvokeStatic( new JSType(TypeSystem.UInt64), new JSFakeMethod("FromNumber", TypeSystem.UInt64, new[] { currentType }, MethodTypeFactory), new[] { ce.Expression }, true ); } } else if (TypeUtil.IsIntegral(currentType)) { if (!TypeUtil.IsIntegral(targetType)) { // Integer -> float conversion newExpression = new JSIntegerToFloatExpression(ce.Expression, targetType); } else if (TypeUtil.SizeOfType(currentType) < TypeUtil.SizeOfType(targetType)) { // Widening integer -> integer conversion newExpression = ce.Expression; } else { newExpression = null; } } else if (TypeUtil.IsIntegral(targetType)) { newExpression = new JSTruncateExpression(ce.Expression); } else if (TypeUtil.SizeOfType(targetType) < TypeUtil.SizeOfType(currentType)) { // Narrowing double -> float conversion newExpression = new JSDoubleToFloatExpression(ce.Expression); } else { // Widening float -> double conversion newExpression = new JSFloatToDoubleExpression(ce.Expression); } } else { // newExpression = JSIL.Cast(ce.Expression, targetType); } if ((newExpression != null) && (newExpression != ce)) { ParentNode.ReplaceChild(ce, newExpression); VisitReplacement(newExpression); } else { // Debugger.Break(); VisitChildren(ce); } }
protected JSInvocationExpression CastToInteger(JSExpression charExpression) { return(JSInvocationExpression.InvokeMethod( JS.charCodeAt, charExpression, new[] { JSLiteral.New(0) }, true )); }
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")) ) { newExpression = new JSBinaryOperatorExpression( JSBinaryOperator.NotEqual, ce.Expression, new JSDefaultValueLiteral(currentType), TypeSystem.Boolean ); } else if ( TypeUtil.IsNumeric(targetType) && TypeUtil.IsNumeric(currentType) ) { 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); } }