public override JSExpression Translate(ILBlockTranslator translator, JSExpression[] arguments) { var thisArgument = FixupThisArgument(arguments[1], translator.TypeSystem); var returnType = ReturnType; if (returnType == null) { returnType = translator.TypeSystem.Void; } var argumentValues = arguments.Skip(2).ToArray(); var memberName = MemberName; if ((TypeArguments != null) && (TypeArguments.Length > 0)) { memberName += "`" + TypeArguments.Length; } return(JSInvocationExpression.InvokeMethod( new JSFakeMethod( memberName, returnType, (from av in argumentValues select av.GetActualType(translator.TypeSystem)).ToArray(), translator.MethodTypes, TypeArguments ), thisArgument, arguments.Skip(2).ToArray() )); }
public JSStatement MaybeReplaceInvocation (JSInvocationExpression invocation) { bool isInControlFlow = Stack.OfType<JSStatement>().Any((n) => n.IsControlFlow); var jsm = invocation.JSMethod; if ( (jsm != null) && (jsm.Method.Name == ".ctor") && TypeUtil.IsStruct(jsm.Method.DeclaringType.Definition) ) { var previousInitialization = Initializations.LastOrDefault( (ne) => ne.ParentBinaryExpression.Left.Equals(invocation.ThisReference) ); if (previousInitialization != null) { bool isDefaultInit = (previousInitialization.DefaultValueLiteral != null) || (previousInitialization.NewExpression.Arguments.Count == 0); if (!previousInitialization.Folded && !isInControlFlow && isDefaultInit) return FoldInvocation(invocation, previousInitialization); if (jsm.Method.DeclaringType.IsImmutable) { // A constructor is being invoked on an immutable struct, which means an existing instance is being mutated. // We need to convert this into a reassignment of the local containing the struct. This creates garbage, but // it's probably still better than not applying immutability optimizations at all. :/ return ConvertInvocationIntoReassignment(invocation, previousInitialization); } } } return null; }
public void VisitNode(JSBinaryOperatorExpression boe) { var leftType = boe.Left.GetActualType(TypeSystem); var leftIsEnum = IsEnumOrNullableEnum(leftType); var rightType = boe.Right.GetActualType(TypeSystem); var rightIsEnum = IsEnumOrNullableEnum(rightType); var resultType = boe.GetActualType(TypeSystem); var resultIsEnum = IsEnumOrNullableEnum(resultType); if ((leftIsEnum || rightIsEnum) && LogicalOperators.Contains(boe.Operator)) { if (leftIsEnum) { var cast = JSInvocationExpression.InvokeStatic( JS.Number(TypeSystem.Int32), new[] { boe.Left }, true ); boe.ReplaceChild(boe.Left, cast); } if (rightIsEnum) { var cast = JSInvocationExpression.InvokeStatic( JS.Number(TypeSystem.Int32), new[] { boe.Right }, true ); boe.ReplaceChild(boe.Right, cast); } } VisitChildren(boe); }
public void VisitNode(JSNewArrayElementReference naer) { var isInsideLoop = (Stack.Any((node) => node is JSLoopStatement)); var parentPassByRef = ParentNode as JSPassByReferenceExpression; var parentInvocation = Stack.OfType <JSInvocationExpression>().FirstOrDefault(); var doesValueEscape = DoesValueEscapeFromInvocation(parentInvocation, naer, true); if ( isInsideLoop && (parentPassByRef != null) && (parentInvocation != null) && !doesValueEscape ) { var replacement = CreateHoistedVariable( (hoistedVariable) => JSInvocationExpression.InvokeMethod( new JSFakeMethod("retarget", hoistedVariable.GetActualType(TypeSystem), new TypeReference[] { TypeSystem.Object, TypeSystem.Int32 }, MethodTypes), hoistedVariable, new JSExpression[] { naer.Array, naer.Index } ), naer.GetActualType(TypeSystem), naer.MakeUntargeted() ); ParentNode.ReplaceChild(naer, replacement); VisitReplacement(replacement); } VisitChildren(naer); }
public void VisitNode(JSInvocationExpression invocation) { var jsm = invocation.JSMethod; MethodInfo method = null; if (jsm != null) { method = jsm.Method; } bool isOverloaded = (method != null) && method.IsOverloadedRecursive && !method.Metadata.HasAttribute("JSIL.Meta.JSRuntimeDispatch"); if (isOverloaded && JavascriptAstEmitter.CanUseFastOverloadDispatch(method)) { isOverloaded = false; } if ((method != null) && method.DeclaringType.IsInterface) { // HACK if (!PackedArrayUtil.IsPackedArrayType(jsm.Reference.DeclaringType)) { CacheInterfaceMember(jsm.Reference.DeclaringType, jsm.Identifier); } } if ((jsm != null) && (method != null) && isOverloaded) { CacheSignature(jsm.Reference, method.Signature, false); } VisitChildren(invocation); }
public JSInvocationExpression GetTypeFromAssembly(JSExpression assembly, JSExpression typeName, JSExpression throwOnFail) { return(JSInvocationExpression.InvokeStatic( Dot("GetTypeFromAssembly", TypeSystem.SystemType()), new[] { assembly, typeName, new JSNullLiteral(TypeSystem.Object), throwOnFail }, true )); }
private JSStatement FoldInvocation(JSInvocationExpression invocation, InitializationInfo ii) { var arguments = invocation.Arguments.ToArray(); var newExpression = new JSNewExpression( ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments ); // Constructor call contains a reference to the struct being initialized. // For some reason the C# compiler lets you do this even though it would be undefined // if not for a nuance in how struct locals work in MSIL. if (newExpression.SelfAndChildrenRecursive.Any((n) => n.Equals(invocation.ThisReference))) { return(null); } ii.Folded = true; ii.BinaryExpressionParent.ReplaceChild(ii.ParentBinaryExpression, new JSNullExpression()); var newBoe = new JSBinaryOperatorExpression( JSOperator.Assignment, ii.ParentBinaryExpression.Left, newExpression, ii.ParentBinaryExpression.ActualType ); return(new JSVariableDeclarationStatement(newBoe)); }
public JSInvocationExpression GetTypeOf(JSExpression expression) { return(JSInvocationExpression.InvokeStatic( Dot("GetType", TypeSystem.SystemType()), new[] { expression }, true )); }
public void VisitNode(JSNewExpression newexp) { var type = newexp.GetActualType(TypeSystem); var isStruct = TypeUtil.IsStruct(type); var isInsideLoop = (Stack.Any((node) => node is JSLoopStatement)); var parentInvocation = ParentNode as JSInvocationExpression; var doesValueEscape = DoesValueEscapeFromInvocation(parentInvocation, newexp, false); if (isStruct && isInsideLoop && (parentInvocation != null) && !doesValueEscape ) { var replacement = CreateHoistedVariable( (hoistedVariable) => new JSCommaExpression( JSInvocationExpression.InvokeMethod( type, new JSMethod(newexp.ConstructorReference, newexp.Constructor, MethodTypes, null), hoistedVariable, newexp.Arguments.ToArray(), false ), hoistedVariable ), type ); ParentNode.ReplaceChild(newexp, replacement); VisitReplacement(replacement); } else { VisitChildren(newexp); } }
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(JSUnaryOperatorExpression uoe) { var exType = uoe.Expression.GetActualType(TypeSystem); var opType = uoe.ActualType; if (IsLongOrULong(exType) && IsLongOrULong(opType)) //exType == TypeSystem.Int64 && opType == TypeSystem.Int64) { string verb; switch (uoe.Operator.Token) { case "-": verb = "op_UnaryNegation"; break; case "~": verb = "op_OnesComplement"; break; default: throw new NotSupportedException(); } var method = new JSFakeMethod(verb, TypeSystem.Int64, new[] { TypeSystem.Int64 }, MethodTypeFactory); var replacement = JSInvocationExpression.InvokeStatic(exType, method, new[] { uoe.Expression }, true); ParentNode.ReplaceChild(uoe, replacement); VisitReplacement(replacement); return; } VisitChildren(uoe); }
public JSExpression Cast(JSExpression expression, TypeReference targetType) { return(JSInvocationExpression.InvokeStatic( Dot("Cast", targetType), new[] { expression, new JSTypeOfExpression(targetType) }, true )); }
public JSInvocationExpression StructEquals(JSExpression left, JSExpression right) { return(JSInvocationExpression.InvokeStatic( Dot("StructEquals", TypeSystem.Boolean), new[] { left, right }, true )); }
protected JSInvocationExpression CastToInteger(JSExpression booleanExpression) { return(JSInvocationExpression.InvokeMethod( JS.valueOf(TypeSystem.SByte), booleanExpression, null, true )); }
public JSInvocationExpression GetTypeOf(JSExpression expression) { return(JSInvocationExpression.InvokeStatic( Dot("GetType", new TypeReference("System", "Type", TypeSystem.Object.Module, TypeSystem.Object.Scope)), new[] { expression }, true )); }
public void VisitNode(JSInvocationExpression ie) { var jsm = ie.JSMethod; if (jsm == null) { Console.WriteLine("Can't translate non-JSMethod {0}", ie); Formatter.WriteSExpr("untranslatable.call"); return; } else if (!jsm.Method.IsStatic) { Console.WriteLine("Can't translate instance call {0}", ie); Formatter.WriteSExpr("untranslatable.call"); return; } var methodDef = jsm.Reference.Resolve(); var memberName = WasmUtil.FormatMemberName(methodDef); Formatter.WriteSExpr("call", (_) => { _.WriteRaw("${0} ", memberName); EmitArgumentList(_, ie.Arguments, false); }); }
public JSInvocationExpression FreezeImmutableObject(JSExpression @object) { return(JSInvocationExpression.InvokeStatic( Dot(new JSFakeMethod("FreezeImmutableObject", TypeSystem.Void, new[] { TypeSystem.Object }, MethodTypes)), new[] { @object } )); }
public JSInvocationExpression CreateInstanceOfType(TypeReference type) { return(JSInvocationExpression.InvokeStatic( Dot(new JSFakeMethod("CreateInstanceOfType", type, new[] { TypeSystem.Object }, MethodTypes)), new[] { new JSTypeOfExpression(type) } )); }
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 JSInvocationExpression ThrowNullReferenceException() { return(JSInvocationExpression.InvokeStatic( Dot(new JSFakeMethod("ThrowNullReferenceException", TypeSystem.Void, new TypeReference[0], MethodTypes)), new JSExpression[0] )); }
public JSInvocationExpression GetTypeFromAssembly(JSExpression assembly, JSExpression typeName, JSExpression throwOnFail) { return(JSInvocationExpression.InvokeStatic( Dot("GetTypeFromAssembly", new TypeReference("System", "Type", TypeSystem.Object.Module, TypeSystem.Object.Scope)), new[] { assembly, typeName, new JSNullLiteral(TypeSystem.Object), throwOnFail }, true )); }
public void VisitNode(JSInvocationExpression ie) { var variables = ExtractAffectedVariables(ie.Method, ie.Parameters); var type = ie.JSType; var thisVar = ExtractAffectedVariable(ie.ThisReference); var method = ie.JSMethod; if (thisVar != null) { ModifiedVariable(thisVar); State.Invocations.Add(new FunctionAnalysis1stPass.Invocation( GetParentNodeIndices(), StatementIndex, NodeIndex, thisVar, method, ie.Method, variables )); } else { State.Invocations.Add(new FunctionAnalysis1stPass.Invocation( GetParentNodeIndices(), StatementIndex, NodeIndex, type, method, ie.Method, variables )); } VisitChildren(ie); }
public JSInvocationExpression CheckType(JSExpression expression, TypeReference targetType) { return(JSInvocationExpression.InvokeStatic( Dot("CheckType", TypeSystem.Boolean), new[] { expression, new JSTypeOfExpression(targetType) }, true )); }
public JSInvocationExpression Coalesce(JSExpression left, JSExpression right, TypeReference expectedType) { return(JSInvocationExpression.InvokeStatic( Dot("Coalesce", expectedType), new[] { left, right }, true )); }
public JSInvocationExpression ShallowCopy(JSExpression array, JSExpression initializer, TypeReference arrayType) { return(JSInvocationExpression.InvokeStatic( new JSDotExpression( Dot("Array", TypeSystem.Object), new JSFakeMethod("ShallowCopy", TypeSystem.Void, new[] { arrayType, arrayType }, MethodTypes) ), new[] { array, initializer } )); }
public JSInvocationExpression NewDelegate(TypeReference delegateType, JSExpression thisReference, JSExpression targetMethod) { return(JSInvocationExpression.InvokeStatic( new JSDotExpression( new JSType(delegateType), new JSFakeMethod("New", delegateType, new[] { TypeSystem.Object, TypeSystem.Object }, MethodTypes) ), new [] { thisReference, targetMethod }, true )); }
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 JSInvocationExpression ValueOfNullable(JSExpression nullableExpression) { var valueType = nullableExpression.GetActualType(TypeSystem); valueType = TypeUtil.StripNullable(valueType); return(JSInvocationExpression.InvokeStatic( Dot("ValueOfNullable", valueType), new[] { nullableExpression }, true )); }
public JSExpression ValueOfNullableOrDefault(JSExpression nullableExpression, JSExpression defaultValue) { var valueType = nullableExpression.GetActualType(TypeSystem); valueType = TypeUtil.StripNullable(valueType); return(JSInvocationExpression.InvokeStatic( Dot("Nullable_ValueOrDefault", valueType), new[] { nullableExpression, defaultValue }, true )); }
public void VisitNode(JSInvocationExpression ie) { if ((ie.GenericArguments != null) && (ie.JSMethod != null)) { ie.JSMethod.SetCachedGenericArguments( from ga in ie.GenericArguments select GetCachedType(ga) ); } VisitChildren(ie); }
public JSInvocationExpression StackAlloc(JSExpression sizeInBytes, TypeReference pointerType) { if (!pointerType.IsPointer) { throw new InvalidOperationException("Type being stack-allocated must be a pointer"); } return(JSInvocationExpression.InvokeStatic( Dot(new JSFakeMethod("StackAlloc", pointerType, new[] { TypeSystem.Int32, TypeSystem.Object }, MethodTypes)), new[] { sizeInBytes, new JSType(TypeUtil.GetElementType(pointerType, true)) } )); }
public JSStatement MaybeReplaceInvocation(JSInvocationExpression invocation) { var jsm = invocation.JSMethod; if ( (jsm != null) && (jsm.Method.Name == ".ctor") && TypeUtil.IsStruct(jsm.Method.DeclaringType.Definition) && !Stack.OfType<JSBlockStatement>().Any((n) => n.IsControlFlow) ) { var previousInitialization = Initializations.LastOrDefault( (ne) => ne.ParentBinaryExpression.Left.Equals(invocation.ThisReference) ); if (previousInitialization != null) return FoldInvocation(invocation, previousInitialization); } return null; }
public void VisitNode(JSInvocationExpression invocation) { for (int i = 0, c = invocation.Arguments.Count; i < c; i++) { var argument = invocation.Arguments[i]; if (IsCopyNeeded(argument)) { if (Tracing) Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument)); invocation.Arguments[i] = new JSStructCopyExpression(argument); } } VisitChildren(invocation); }
public void VisitNode(JSInvocationExpression invocation) { FunctionAnalysis2ndPass sa = null; if (invocation.JSMethod != null) sa = FunctionSource.GetSecondPass(invocation.JSMethod); var parms = invocation.Parameters.ToArray(); for (int i = 0, c = parms.Length; i < c; i++) { var pd = parms[i].Key; var argument = parms[i].Value; string parameterName = null; if (pd != null) parameterName = pd.Name; if (IsParameterCopyNeeded(sa, parameterName, argument)) { if (Tracing) Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument)); invocation.Arguments[i] = new JSStructCopyExpression(argument); } else { if (Tracing) Debug.WriteLine(String.Format("struct copy elided for argument {0}", argument)); } } VisitChildren(invocation); }
public void VisitNode(JSInvocationExpression invocation) { FunctionAnalysis2ndPass sa = null; if (invocation.JSMethod != null) sa = FunctionSource.GetSecondPass(invocation.JSMethod); var parms = invocation.Parameters.ToArray(); for (int i = 0, c = parms.Length; i < c; i++) { var pd = parms[i].Key; var argument = parms[i].Value; string parameterName = null; if (pd != null) parameterName = pd.Name; GenericParameter relevantParameter; if (IsParameterCopyNeeded(sa, parameterName, argument, out relevantParameter)) { if (Tracing) Debug.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument)); invocation.Arguments[i] = MakeCopyForExpression(argument, relevantParameter); } else { if (Tracing && TypeUtil.IsStruct(argument.GetActualType(TypeSystem))) Debug.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument)); } } var thisReference = invocation.ThisReference; if ( (thisReference != null) && (sa != null) && sa.ViolatesThisReferenceImmutability && !(ParentNode is JSCommaExpression) ) { // The method we're calling violates immutability so we need to clone the this-reference // before we call it. var thisReferenceType = thisReference.GetActualType(TypeSystem); if (TypeUtil.IsStruct(thisReferenceType)) { if (!(thisReference is JSVariable) && !(thisReference is JSFieldAccess)) throw new NotImplementedException("Unsupported invocation of method that reassigns this within an immutable struct: " + invocation.ToString()); var cloneExpr = new JSBinaryOperatorExpression( JSOperator.Assignment, thisReference, new JSStructCopyExpression(thisReference), thisReferenceType ); var commaExpression = new JSCommaExpression(cloneExpr, invocation); ParentNode.ReplaceChild(invocation, commaExpression); VisitReplacement(commaExpression); return; } } VisitChildren(invocation); }
private JSStatement FoldInvocation(JSInvocationExpression invocation, InitializationInfo ii) { var arguments = invocation.Arguments.ToArray(); var newExpression = new JSNewExpression( ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments ); ii.BinaryExpressionParent.ReplaceChild(ii.ParentBinaryExpression, new JSNullExpression()); Initializations.Remove(ii); var newBoe = new JSBinaryOperatorExpression( JSOperator.Assignment, ii.ParentBinaryExpression.Left, newExpression, ii.ParentBinaryExpression.ActualType ); return new JSVariableDeclarationStatement(newBoe); }
public void VisitNode(JSInvocationExpression invocation) { FunctionAnalysis2ndPass sa = null; if (invocation.JSMethod != null) sa = FunctionSource.GetSecondPass(invocation.JSMethod); var parms = invocation.Parameters.ToArray(); for (int i = 0, c = parms.Length; i < c; i++) { var pd = parms[i].Key; var argument = parms[i].Value; if (IsCopyNeeded(argument)) { bool modified = true, escapes = true, isResult = false; if ((sa != null) && (pd != null)) { var varName = pd.Name; modified = sa.ModifiedVariables.Contains(varName); escapes = sa.EscapingVariables.Contains(varName); isResult = sa.ResultVariable == varName; } if (modified || (escapes && !isResult)) { if (Tracing) Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument)); invocation.Arguments[i] = new JSStructCopyExpression(argument); } else { if (Tracing) Debug.WriteLine(String.Format("struct copy elided for argument {0}", argument)); } } } VisitChildren(invocation); }
public void VisitNode (JSInvocationExpression invocation) { FunctionAnalysis2ndPass sa = null; if (invocation.JSMethod != null) sa = GetSecondPass(invocation.JSMethod); CloneArgumentsIfNecessary(invocation.Parameters, invocation.Arguments, sa); var thisReference = invocation.ThisReference; if ( (thisReference != null) && (sa != null) && sa.ViolatesThisReferenceImmutability && !(ParentNode is JSCommaExpression) ) { // The method we're calling violates immutability so we need to clone the this-reference // before we call it. var thisReferenceType = thisReference.GetActualType(TypeSystem); if (TypeUtil.IsStruct(thisReferenceType)) { if (!(thisReference is JSVariable) && !(thisReference is JSFieldAccess)) throw new NotImplementedException("Unsupported invocation of method that reassigns this within an immutable struct: " + invocation.ToString()); var cloneExpr = new JSBinaryOperatorExpression( JSOperator.Assignment, thisReference, new JSStructCopyExpression(thisReference), thisReferenceType ); var commaExpression = new JSCommaExpression(cloneExpr, invocation); ParentNode.ReplaceChild(invocation, commaExpression); VisitReplacement(commaExpression); return; } } VisitChildren(invocation); }
public void VisitNode(JSInvocationExpression invocation) { FunctionAnalysis2ndPass sa = null; if (invocation.JSMethod != null) sa = FunctionSource.GetSecondPass(invocation.JSMethod); var parms = invocation.Parameters.ToArray(); for (int i = 0, c = parms.Length; i < c; i++) { var pd = parms[i].Key; var argument = parms[i].Value; string parameterName = null; if (pd != null) parameterName = pd.Name; GenericParameter relevantParameter; if (IsParameterCopyNeeded(sa, parameterName, argument, out relevantParameter)) { if (Tracing) Debug.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument)); invocation.Arguments[i] = MakeCopyForExpression(argument, relevantParameter); } else { if (Tracing && TypeUtil.IsStruct(argument.GetActualType(TypeSystem))) Debug.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument)); } } VisitChildren(invocation); }
public void VisitNode (JSInvocationExpression invocation) { FunctionAnalysis2ndPass sa = null; if (invocation.JSMethod != null) sa = GetSecondPass(invocation.JSMethod); CloneArgumentsIfNecessary(invocation.Parameters, invocation.Arguments, sa); var thisReference = invocation.ThisReference; var thisReferenceType = thisReference.GetActualType(TypeSystem); var thisReferenceIsStruct = TypeUtil.IsStruct(thisReferenceType) && !TypeUtil.IsNullable(thisReferenceType); bool thisReferenceNeedsCopy = false; bool thisReferenceNeedsCopyAndReassignment = false; if (thisReferenceIsStruct && !invocation.SuppressThisClone) { var isMethodInvocation = (thisReference != null) && (sa != null) && !(ParentNode is JSCommaExpression) && !(thisReference is JSStructCopyExpression) && !( (ParentNode is JSResultReferenceExpression) && Stack.OfType<JSCommaExpression>().Any() ); // If a struct is immutable, a method may reassign the this-reference, // i.e. 'this = otherstruct' successfully. // In this scenario we have to clone the old this-reference, invoke // the method on the clone, and replace the old this-reference with // the new, modified clone. thisReferenceNeedsCopyAndReassignment = isMethodInvocation && sa.ViolatesThisReferenceImmutability; // When invoking a method that mutates a struct's members or lets them escape, // we need to copy the this-reference if it isn't writable. // FIXME: We're white-listing writable targets, but we probably want to blacklist // non-writable targets instead, so that if a fn's result is new we don't clone it // to use it as a this-reference. // FIXME: Handle pointers, replace x.get().foo() with some sort of comma expr, // like ($x = x.get(), $x.foo(), x.set($x)) ? var isWritableInstance = (thisReference is JSFieldAccess) || (thisReference is JSVariable) || (thisReference is JSReadThroughReferenceExpression); thisReferenceNeedsCopy = isMethodInvocation && ( sa.IsVariableModified("this") || // Maybe don't include return here? sa.DoesVariableEscape("this", true) ) && (!isWritableInstance); } GenericParameter relevantParameter; var isCopyNeeded = IsCopyNeeded(thisReference, out relevantParameter, false); if ( thisReferenceNeedsCopyAndReassignment && isCopyNeeded ) { if (TraceInsertedCopies) Console.WriteLine("Cloning this-reference because method reassigns this: {0}", invocation); if ( (thisReference is JSFieldAccess) || (thisReference is JSVariable) ) { var rre = ParentNode as JSResultReferenceExpression; var cloneExpr = new JSBinaryOperatorExpression( JSOperator.Assignment, thisReference, MakeCopyForExpression(thisReference, relevantParameter), thisReferenceType ); var commaExpression = new JSCommaExpression( cloneExpr, (rre != null) ? (JSExpression)new JSResultReferenceExpression(invocation) : (JSExpression)invocation ); if (rre != null) { ResultReferenceReplacement = commaExpression; return; } else { ParentNode.ReplaceChild(invocation, commaExpression); VisitReplacement(commaExpression); return; } } else { // Huh? invocation.ReplaceChild(thisReference, MakeCopyForExpression(thisReference, relevantParameter)); VisitChildren(invocation); return; } } else if (thisReferenceNeedsCopy && isCopyNeeded) { if (TraceInsertedCopies) Console.WriteLine("Cloning this-reference because method mutates this and this-reference is not field/local: {0}", invocation); invocation.ReplaceChild(thisReference, MakeCopyForExpression(thisReference, relevantParameter)); VisitChildren(invocation); return; } VisitChildren(invocation); }
bool DoesValueEscapeFromInvocation (JSInvocationExpression invocation, JSExpression argumentExpression, bool includeReturn) { if ( (invocation != null) && (invocation.JSMethod != null) && (invocation.JSMethod.Reference != null) ) { var methodDef = invocation.JSMethod.Reference.Resolve(); var secondPass = FunctionSource.GetSecondPass(invocation.JSMethod, Function.Method.QualifiedIdentifier); if ((secondPass != null) && (methodDef != null)) { // HACK var argumentIndex = invocation.Arguments.Select( (a, i) => new { argument = a, index = i }) .FirstOrDefault((_) => _.argument.SelfAndChildrenRecursive.Contains(argumentExpression)); if (argumentIndex != null) { var argumentName = methodDef.Parameters[argumentIndex.index].Name; return secondPass.DoesVariableEscape(argumentName, includeReturn); } } else if (secondPass != null) { // HACK for methods that do not have resolvable references. In this case, if NONE of their arguments escape, we're probably still okay. if (secondPass.GetNumberOfEscapingVariables(includeReturn) == 0) return false; } } return true; }
public void VisitNode(JSInvocationExpression ie) { var jsm = ie.JSMethod; // Detect direct invocations of getter methods if ( (jsm != null) && (jsm.Method.Property == Property.Property) && (ie.ThisReference.Equals(ThisReference)) && (ie.Arguments.Count == 0) ) { ParentNode.ReplaceChild(ie, Replacement); VisitReplacement(Replacement); ReplacedInvocation = true; } else { VisitChildren(ie); } }
public void VisitNode (JSInvocationExpression ie) { var type = ie.JSType; var method = ie.JSMethod; var thisExpression = ie.ThisReference; if (method != null) { if ( (type != null) && (type.Type.FullName == "System.Object") ) { switch (method.Method.Member.Name) { case ".ctor": { var replacement = new JSNullExpression(); ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } case "ReferenceEquals": { var lhs = ie.Arguments[0]; var rhs = ie.Arguments[1]; var lhsType = lhs.GetActualType(TypeSystem); var rhsType = rhs.GetActualType(TypeSystem); JSNode replacement; // Structs can never compare equal with ReferenceEquals if (TypeUtil.IsStruct(lhsType) || TypeUtil.IsStruct(rhsType)) replacement = JSLiteral.New(false); else replacement = new JSBinaryOperatorExpression( JSOperator.Equal, lhs, rhs, TypeSystem.Boolean ); ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } case "GetType": { JSNode replacement; var thisType = JSExpression.DeReferenceType(thisExpression.GetActualType(TypeSystem), false); if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) { var git = (GenericInstanceType)thisType; replacement = new JSTernaryOperatorExpression( new JSBinaryOperatorExpression( JSOperator.NotEqual, thisExpression, new JSNullLiteral(thisType), TypeSystem.Boolean ), new JSTypeOfExpression(git.GenericArguments[0]), JSIL.ThrowNullReferenceException(), TypeSystem.SystemType() ); } else { replacement = JSIL.GetTypeOf(thisExpression); } ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } } else if ( (type != null) && (type.Type.FullName == "System.ValueType") ) { switch (method.Method.Member.Name) { case "Equals": { var replacement = JSIL.StructEquals(ie.ThisReference, ie.Arguments.First()); ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } } else if ( (type != null) && IsNullable(type.Type) ) { var t = (type.Type as GenericInstanceType).GenericArguments[0]; var @null = JSLiteral.Null(t); var @default = new JSDefaultValueLiteral(t); switch (method.Method.Member.Name) { case ".ctor": JSExpression value; if (ie.Arguments.Count == 0) { value = @null; } else { value = ie.Arguments[0]; } var boe = new JSBinaryOperatorExpression( JSOperator.Assignment, ie.ThisReference, value, type.Type ); ParentNode.ReplaceChild(ie, boe); VisitReplacement(boe); break; case "GetValueOrDefault": { var replacement = JSIL.ValueOfNullableOrDefault( ie.ThisReference, (ie.Arguments.Count == 0) ? @default : ie.Arguments[0] ); if (ParentNode is JSResultReferenceExpression) { // HACK: Replacing the invocation inside a result reference is incorrect, so we need to walk up the stack // and replace the result reference with the ternary instead. _ResultReferenceReplacement = replacement; } else { ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); } break; } case "Equals": JSBinaryOperatorExpression equality = new JSBinaryOperatorExpression(JSOperator.Equal, ie.ThisReference, ie.Parameters.First().Value, type.Type); ParentNode.ReplaceChild(ie, equality); VisitReplacement(equality); break; default: throw new NotImplementedException(method.Method.Member.FullName); } return; } else if ( (type != null) && TypeUtil.TypesAreEqual(TypeSystem.String, type.Type) && (method.Method.Name == "Concat") ) { if (ie.Arguments.Count > 2) { if (ie.Arguments.All( (arg) => TypeUtil.TypesAreEqual( TypeSystem.String, arg.GetActualType(TypeSystem) ) )) { var boe = JSBinaryOperatorExpression.New( JSOperator.Add, ie.Arguments, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } } else if ( // HACK: Fix for #239, only convert concat call into + if both sides are non-null literals (ie.Arguments.Count == 2) ) { var lhs = ie.Arguments[0]; var rhs = ie.Arguments[1]; var isAddOk = (lhs is JSStringLiteral) && (rhs is JSStringLiteral); var lhsType = TypeUtil.DereferenceType(lhs.GetActualType(TypeSystem)); if (!( TypeUtil.TypesAreEqual(TypeSystem.String, lhsType) || TypeUtil.TypesAreEqual(TypeSystem.Char, lhsType) )) { lhs = JSInvocationExpression.InvokeMethod(lhsType, JS.toString, lhs, null); isAddOk = true; } var rhsType = TypeUtil.DereferenceType(rhs.GetActualType(TypeSystem)); if (!( TypeUtil.TypesAreEqual(TypeSystem.String, rhsType) || TypeUtil.TypesAreEqual(TypeSystem.Char, rhsType) )) { rhs = JSInvocationExpression.InvokeMethod(rhsType, JS.toString, rhs, null); isAddOk = true; } if (isAddOk) { var boe = new JSBinaryOperatorExpression( JSOperator.Add, lhs, rhs, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } } else if ( TypeUtil.GetTypeDefinition(ie.Arguments[0].GetActualType(TypeSystem)).FullName == "System.Array" ) { } else { var firstArg = ie.Arguments.FirstOrDefault(); ParentNode.ReplaceChild( ie, firstArg ); if (firstArg != null) VisitReplacement(firstArg); } return; } else if ( TypeUtil.IsDelegateType(method.Reference.DeclaringType) && (method.Method.Name == "Invoke") ) { var newIe = new JSDelegateInvocationExpression( thisExpression, ie.GetActualType(TypeSystem), ie.Arguments.ToArray() ); ParentNode.ReplaceChild(ie, newIe); VisitReplacement(newIe); return; } else if ( (method.Reference.DeclaringType.Name == "RuntimeHelpers") && (method.Method.Name == "InitializeArray") ) { var array = ie.Arguments[0]; var arrayType = array.GetActualType(TypeSystem); var field = ie.Arguments[1].SelfAndChildrenRecursive.OfType<JSField>().First(); var initializer = JSArrayExpression.UnpackArrayInitializer(arrayType, field.Field.Member.InitialValue); var copy = JSIL.ShallowCopy(array, initializer, arrayType); ParentNode.ReplaceChild(ie, copy); VisitReplacement(copy); return; } else if ( method.Reference.DeclaringType.FullName == "System.Reflection.Assembly" ) { switch (method.Reference.Name) { case "GetExecutingAssembly": { var assembly = Method.DeclaringType.Module.Assembly; var asmNode = new JSReflectionAssembly(assembly); ParentNode.ReplaceChild(ie, asmNode); VisitReplacement(asmNode); return; } } } else if ( method.Method.DeclaringType.Definition.FullName == "System.Array" && (ie.Arguments.Count == 1) ) { switch (method.Method.Name) { case "GetLength": case "GetUpperBound": { var index = ie.Arguments[0] as JSLiteral; if (index != null) { var newDot = JSDotExpression.New(thisExpression, new JSStringIdentifier( String.Format("length{0}", Convert.ToInt32(index.Literal), true), TypeSystem.Int32 )); if (method.Method.Name == "GetUpperBound") { var newExpr = new JSBinaryOperatorExpression( JSOperator.Subtract, newDot, JSLiteral.New(1), TypeSystem.Int32 ); ParentNode.ReplaceChild(ie, newExpr); } else { ParentNode.ReplaceChild(ie, newDot); } } break; } case "GetLowerBound": ParentNode.ReplaceChild(ie, JSLiteral.New(0)); break; } } } VisitChildren(ie); }
private JSStatement FoldInvocation (JSInvocationExpression invocation, InitializationInfo ii) { var arguments = invocation.Arguments.ToArray(); var newExpression = new JSNewExpression( ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments ); // Constructor call contains a reference to the struct being initialized. // For some reason the C# compiler lets you do this even though it would be undefined // if not for a nuance in how struct locals work in MSIL. if (newExpression.SelfAndChildrenRecursive.Any((n) => n.Equals(invocation.ThisReference))) return null; ii.Folded = true; ii.BinaryExpressionParent.ReplaceChild(ii.ParentBinaryExpression, new JSNullExpression()); var newBoe = new JSBinaryOperatorExpression( JSOperator.Assignment, ii.ParentBinaryExpression.Left, newExpression, ii.ParentBinaryExpression.ActualType ); return new JSVariableDeclarationStatement(newBoe); }
public void VisitNode(JSInvocationExpression ie) { var type = ie.JSType; var method = ie.JSMethod; var thisExpression = ie.ThisReference; if (method != null) { if ( (type != null) && (type.Type.FullName == "System.Object") ) { switch (method.Method.Member.Name) { case "GetType": JSNode replacement; var thisType = JSExpression.DeReferenceType(thisExpression.GetExpectedType(TypeSystem), false); if ((thisType is GenericInstanceType) && thisType.FullName.StartsWith("System.Nullable")) { replacement = new JSType(thisType); } else { replacement = JSIL.GetType(thisExpression); } ParentNode.ReplaceChild(ie, replacement); VisitReplacement(replacement); return; } } else if ( (type != null) && (type.Type.FullName.StartsWith("System.Nullable")) && (type.Type is GenericInstanceType) ) { var t = (type.Type as GenericInstanceType).GenericArguments[0]; var @null = JSLiteral.Null(t); var @default = new JSDefaultValueLiteral(t); switch (method.Method.Member.Name) { case ".ctor": JSExpression value; if (ie.Arguments.Count == 0) { value = @null; } else { value = ie.Arguments[0]; } var boe = new JSBinaryOperatorExpression( JSOperator.Assignment, ie.ThisReference, value, type.Type ); ParentNode.ReplaceChild(ie, boe); VisitReplacement(boe); break; case "GetValueOrDefault": var isNull = new JSBinaryOperatorExpression( JSOperator.Equal, ie.ThisReference, @null, TypeSystem.Boolean ); JSTernaryOperatorExpression ternary; if (ie.Arguments.Count == 0) { ternary = new JSTernaryOperatorExpression( isNull, @default, ie.ThisReference, type.Type ); } else { ternary = new JSTernaryOperatorExpression( isNull, ie.Arguments[0], ie.ThisReference, type.Type ); } ParentNode.ReplaceChild(ie, ternary); VisitReplacement(ternary); break; default: throw new NotImplementedException(method.Method.Member.FullName); } return; } else if ( (type != null) && ILBlockTranslator.TypesAreEqual(TypeSystem.String, type.Type) && (method.Method.Name == "Concat") ) { if (ie.Arguments.Count > 2) { if (ie.Arguments.All( (arg) => ILBlockTranslator.TypesAreEqual( TypeSystem.String, arg.GetExpectedType(TypeSystem) ) )) { var boe = JSBinaryOperatorExpression.New( JSOperator.Add, ie.Arguments, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } } else if ( ie.Arguments.Count == 2 ) { var lhs = ie.Arguments[0]; var lhsType = ILBlockTranslator.DereferenceType(lhs.GetExpectedType(TypeSystem)); if (!( ILBlockTranslator.TypesAreEqual(TypeSystem.String, lhsType) || ILBlockTranslator.TypesAreEqual(TypeSystem.Char, lhsType) )) { lhs = JSInvocationExpression.InvokeMethod(lhsType, JS.toString, lhs, null); } var rhs = ie.Arguments[1]; var rhsType = ILBlockTranslator.DereferenceType(rhs.GetExpectedType(TypeSystem)); if (!( ILBlockTranslator.TypesAreEqual(TypeSystem.String, rhsType) || ILBlockTranslator.TypesAreEqual(TypeSystem.Char, rhsType) )) { rhs = JSInvocationExpression.InvokeMethod(rhsType, JS.toString, rhs, null); } var boe = new JSBinaryOperatorExpression( JSOperator.Add, lhs, rhs, TypeSystem.String ); ParentNode.ReplaceChild( ie, boe ); VisitReplacement(boe); } else if ( ILBlockTranslator.GetTypeDefinition(ie.Arguments[0].GetExpectedType(TypeSystem)).FullName == "System.Array" ) { } else { var firstArg = ie.Arguments.FirstOrDefault(); ParentNode.ReplaceChild( ie, firstArg ); if (firstArg != null) VisitReplacement(firstArg); } return; } else if ( ILBlockTranslator.IsDelegateType(method.Reference.DeclaringType) && (method.Method.Name == "Invoke") ) { var newIe = new JSDelegateInvocationExpression( thisExpression, ie.GetExpectedType(TypeSystem), ie.Arguments.ToArray() ); ParentNode.ReplaceChild(ie, newIe); VisitReplacement(newIe); return; } else if ( (method.Reference.DeclaringType.FullName == "System.Type") && (method.Method.Name == "GetTypeFromHandle") ) { var typeObj = ie.Arguments.FirstOrDefault(); ParentNode.ReplaceChild(ie, typeObj); VisitReplacement(typeObj); return; } else if ( (method.Reference.DeclaringType.Name == "RuntimeHelpers") && (method.Method.Name == "InitializeArray") ) { var array = ie.Arguments[0]; var arrayType = array.GetExpectedType(TypeSystem); var field = ie.Arguments[1].SelfAndChildrenRecursive.OfType<JSField>().First(); var initializer = JSArrayExpression.UnpackArrayInitializer(arrayType, field.Field.Member.InitialValue); var copy = JSIL.ShallowCopy(array, initializer, arrayType); ParentNode.ReplaceChild(ie, copy); VisitReplacement(copy); return; } else if ( method.Method.DeclaringType.Definition.FullName == "System.Array" && (ie.Arguments.Count == 1) ) { switch (method.Method.Name) { case "GetLength": case "GetUpperBound": { var index = ie.Arguments[0] as JSLiteral; if (index != null) { var newDot = JSDotExpression.New(thisExpression, new JSStringIdentifier( String.Format("length{0}", Convert.ToInt32(index.Literal)), TypeSystem.Int32 )); if (method.Method.Name == "GetUpperBound") { var newExpr = new JSBinaryOperatorExpression( JSOperator.Subtract, newDot, JSLiteral.New(1), TypeSystem.Int32 ); ParentNode.ReplaceChild(ie, newExpr); } else { ParentNode.ReplaceChild(ie, newDot); } } break; } case "GetLowerBound": ParentNode.ReplaceChild(ie, JSLiteral.New(0)); break; } } } VisitChildren(ie); }
private JSStatement ConvertInvocationIntoReassignment (JSInvocationExpression invocation, InitializationInfo ii) { var arguments = invocation.Arguments.ToArray(); var newExpression = new JSNewExpression( ii.Type, invocation.JSMethod.Reference, invocation.JSMethod.Method, arguments ); var newBoe = new JSBinaryOperatorExpression( JSOperator.Assignment, ii.ParentBinaryExpression.Left, newExpression, ii.ParentBinaryExpression.ActualType ); return new JSExpressionStatement(newBoe); }