internal JSFunctionExpression Create( MethodInfo info, MethodDefinition methodDef, MethodReference method, QualifiedMemberIdentifier identifier, ILBlockTranslator translator, IEnumerable <JSVariable> parameters, JSBlockStatement body ) { return(Cache.GetOrCreate(identifier, () => { var result = new JSFunctionExpression( new JSMethod(method, info, MethodTypes), translator.Variables, parameters, body, MethodTypes ); OptimizationQueue.TryEnqueue(identifier); return new Entry { Identifier = identifier, Info = info, Reference = method, Expression = result, Variables = translator.Variables, ParameterNames = translator.ParameterNames, SpecialIdentifiers = translator.SpecialIdentifiers }; }).Expression); }
protected void TransformParameterIntoReference(JSVariable parameter, JSBlockStatement block) { var newParameter = new JSParameter("$" + parameter.Identifier, parameter.IdentifierType, parameter.Function); var newVariable = new JSVariable(parameter.Identifier, new ByReferenceType(parameter.IdentifierType), parameter.Function); var newDeclaration = new JSVariableDeclarationStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, // We have to use parameter here, not newVariable or newParameter, otherwise the resulting // assignment looks like 'x.value = initializer' instead of 'x = initializer' parameter, JSIL.NewReference(newParameter), newVariable.IdentifierType ) ); if (Tracing) { Console.WriteLine(String.Format("Transformed {0} into {1}={2}", parameter, newVariable, newParameter)); } Variables[newVariable.Identifier] = newVariable; Variables.Add(newParameter.Identifier, newParameter); var enclosingFunction = Stack.OfType <JSFunctionExpression>().First(); enclosingFunction.Body.Statements.Insert(0, newDeclaration); var oldIndex = Array.IndexOf(enclosingFunction.Parameters, parameter); enclosingFunction.Parameters[oldIndex] = newParameter; }
public void EnsureLabelGroupExists(Dictionary <JSStatement, JSLabelGroupStatement> labelGroups) { if (LabelGroup != null) { return; } if (!labelGroups.TryGetValue(EnclosingBlock.Block, out LabelGroup)) { int index = labelGroups.Count; var entryBlock = new JSBlockStatement { Label = String.Format("$entry{0}", index) }; labelGroups[EnclosingBlock.Block] = LabelGroup = new JSLabelGroupStatement(index, entryBlock); var bs = EnclosingBlock.Block as JSBlockStatement; if (bs != null) { entryBlock.Statements.AddRange(bs.Statements); bs.Statements.Clear(); bs.Statements.Add(LabelGroup); } else { throw new NotImplementedException("Unsupported enclosing block type"); } } }
public void NodeSelfAndChildrenRecursive () { var fl3 = new JSForLoop( new JSNullStatement(), new JSNullExpression(), new JSNullStatement(), new JSExpressionStatement(new JSStringIdentifier("loop3 iteration")) ); fl3.Index = 2; var fl2 = new JSForLoop( new JSNullStatement(), new JSNullExpression(), new JSNullStatement(), new JSExpressionStatement(new JSStringIdentifier("loop2 iteration")) ); fl2.Index = 1; var fl1 = new JSForLoop( new JSNullStatement(), new JSNullExpression(), new JSNullStatement(), fl2 ); fl1.Index = 0; var tree = new JSBlockStatement( fl1, fl3 ); DumpNodeSequence(tree.SelfAndChildrenRecursive); }
public void VisitNode(JSBlockStatement block) { SeenRetargetsInScope.Push(new Dictionary <RetargetKey, int>()); ScopeNodeIndices.Push(NodeIndex); VisitChildren(block); SeenRetargetsInScope.Pop(); ScopeNodeIndices.Pop(); }
public void VisitNode(JSBlockStatement bs) { CheckLabel(bs); PushBlock(bs); try { VisitChildren(bs); } finally { BlockStack.Pop(); } }
internal JSFunctionExpression Create( MethodInfo info, MethodDefinition methodDef, MethodReference method, QualifiedMemberIdentifier identifier, ILBlockTranslator translator, JSVariable[] parameters, JSBlockStatement body ) { var args = new PopulatedCacheEntryArgs { Info = info, Method = method, Translator = translator, Parameters = parameters, Body = body, }; return(Cache.GetOrCreate(identifier, args, MakePopulatedCacheEntry).Expression); }
public void VisitNode(JSBlockStatement block, bool includeBraces) { if (includeBraces) { Output.OpenBrace(); } for (var i = 0; i < block.Statements.Count; i++) { Visit(block.Statements[i]); } if (includeBraces) { Output.CloseBrace(); } }
public void VisitNode(JSBlockStatement bs) { RemoveNullStatements(bs); if ((bs.Statements.Count == 0) && (bs.Label == null)) { var newNull = new JSNullStatement(); ParentNode.ReplaceChild(bs, newNull); VisitReplacement(newNull); return; } VisitChildren(bs); // Some of the children may have replaced themselves with nulls RemoveNullStatements(bs); }
public void VisitNode(JSBlockStatement bs) { CheckForFallthrough(bs); var lastSwitchCase = LastSwitchCase; var thisSwitchCase = ParentSwitchCase; LastSwitchCase = thisSwitchCase; var parentLabelGroup = ParentNode as JSLabelGroupStatement; var isControlFlow = bs.IsControlFlow || (thisSwitchCase != lastSwitchCase) || (parentLabelGroup != null); if (TraceLevel >= 3) { Console.WriteLine("// Entering block {0}", bs.Label ?? bs.GetType().Name); } if (isControlFlow) { if (TraceLevel >= 3) { Console.WriteLine("// Count reset"); } AbsoluteJumpsSeenStack.Add(0); } BlockStack.Push(bs); VisitChildren(bs); BlockStack.Pop(); if (TraceLevel >= 3) { Console.WriteLine("// Exiting block"); } if (isControlFlow) { AbsoluteJumpsSeenStack.RemoveAt(AbsoluteJumpsSeenStack.Count - 1); } }
protected void RemoveNullStatements(JSBlockStatement bs) { bs.Statements.RemoveAll( (s) => { var es = s as JSExpressionStatement; if ((es != null) && es.Expression.IsNull) { return(true); } if (s.IsNull) { return(true); } return(false); } ); }
public void VisitNode(JSBlockStatement block) { VisitNode(block, false); }
private void ExtractExitLabel(JSLabelGroupStatement lgs) { var exitLabel = lgs.ExitLabel; var originalLabelName = exitLabel.Label; if (exitLabel.AllChildrenRecursive.OfType <JSGotoExpression>().Any()) { if (TraceLevel >= 1) { Console.WriteLine("// Cannot extract exit label '{0}' from label group because it contains a goto or exit", originalLabelName); } return; } // The label before this label may have fallen through, so we need to append an ExitLabelGroup var previousLabel = lgs.BeforeExitLabel; if (previousLabel != null) { var exitStatement = new JSExpressionStatement(new JSExitLabelGroupExpression(lgs)); var previousBlock = previousLabel as JSBlockStatement; if (previousBlock != null) { previousBlock.Statements.Add(exitStatement); } else { var replacement = new JSBlockStatement( previousLabel, exitStatement ); replacement.Label = previousLabel.Label; replacement.IsControlFlow = true; previousLabel.Label = null; lgs.ReplaceChild(previousLabel, replacement); } } lgs.Labels.Remove(originalLabelName); exitLabel.Label = null; exitLabel.IsControlFlow = false; { var replacement = new JSBlockStatement( lgs, exitLabel ); exitLabel.OriginalLabel = originalLabelName; // Extract the exit label so it directly follows the label group ParentNode.ReplaceChild(lgs, replacement); // Find and convert all the gotos so that they instead break out of the label group var gotos = DeoptimizeSwitchStatements.FindGotos(lgs, originalLabelName); foreach (var g in gotos) { lgs.ReplaceChildRecursive(g, new JSExitLabelGroupExpression(lgs)); } } if (TraceLevel >= 1) { Console.WriteLine("// Extracted exit label '{0}' from label group", originalLabelName); } MadeChanges = true; }
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 EnsureLabelGroupExists(Dictionary <JSStatement, JSLabelGroupStatement> labelGroups) { if (LabelGroup != null) { return; } if (!labelGroups.TryGetValue(EnclosingBlock.Block, out LabelGroup)) { int index = labelGroups.Count; var entryBlock = new JSBlockStatement { Label = String.Format("$entry{0}", index) }; var exitBlock = new JSBlockStatement { Label = String.Format("$exit{0}", index) }; labelGroups[EnclosingBlock.Block] = LabelGroup = new JSLabelGroupStatement( index, entryBlock, exitBlock ); var bs = EnclosingBlock.Block as JSBlockStatement; if (bs != null) { bool populatingEntryBlock = true, populatingExitBlock = false; // We want to walk through the block's statements and collect them. // Statements before the labelled statement go into the entry block. // Statements after the labelled statement go into the exit block. // FIXME: Is the following rule correct? Without it, FaultBlock breaks. // If we hit another labelled statement while filling the exit block, stop filling it. for (var i = 0; i < bs.Statements.Count; i++) { var s = bs.Statements[i]; if (s.Label == Label) { populatingEntryBlock = false; populatingExitBlock = true; } else if (populatingEntryBlock) { entryBlock.Statements.Add(s); } else if (populatingExitBlock) { if (s.Label == null) { exitBlock.Statements.Add(s); } else { populatingExitBlock = false; } } } bs.Statements.Clear(); bs.Statements.Add(LabelGroup); } else { throw new NotImplementedException("Unsupported enclosing block type"); } } }
public void EnsureLabelGroupExists(Dictionary <JSStatement, JSLabelGroupStatement> labelGroups) { if (LabelGroup != null) { return; } if (!labelGroups.TryGetValue(EnclosingBlock.Block, out LabelGroup)) { int index = labelGroups.Count; var entryBlock = new JSBlockStatement { Label = String.Format("$entry{0}", index) }; var exitBlock = new JSBlockStatement { Label = String.Format("$exit{0}", index) }; labelGroups[EnclosingBlock.Block] = LabelGroup = new JSLabelGroupStatement( index, entryBlock, exitBlock ); var bs = EnclosingBlock.Block as JSBlockStatement; if (bs != null) { bool populatingEntryBlock = true, populatingExitBlock = false; // We want to walk through the block's statements and collect them. // Statements before the labelled statement go into the entry block. // Statements after the labelled statement go into the exit block. // FIXME: Is the following rule correct? Without it, FaultBlock breaks. // If we hit another labelled statement while filling the exit block, stop filling it. for (var i = 0; i < bs.Statements.Count; i++) { var s = bs.Statements[i]; if (s.Label == Label) { populatingEntryBlock = false; populatingExitBlock = true; } else if (populatingEntryBlock) { entryBlock.Statements.Add(s); } else if (populatingExitBlock) { if (s.Label == null) { exitBlock.Statements.Add(s); } // HACK: The synthesized switch exit labels generated when hoisting a block out of // a switch statement shouldn't terminate statement collection. Without this hack, // ForeachInEnumeratorFunctionMonoBinary fails. else if (s.Label.StartsWith("$switchExit")) { ; } else { populatingExitBlock = false; } } } // FIXME: Is this valid? It might confuse the AstVisitor if bs is on the stack. bs.Statements.Clear(); bs.Statements.Add(LabelGroup); } else { throw new NotImplementedException("Unsupported enclosing block type: " + EnclosingBlock.Block.GetType().Name); } } }
public void VisitNode(JSBlockStatement bs) { MaybeHoist(bs, bs.Children.OfType <JSStatement>()); VisitChildren(bs); }
public void VisitNode(JSSwitchStatement ss) { IndexLookup indexLookup; Initializer initializer; NullCheck nullCheck; // Detect switch statements using a lookup dictionary var switchVar = ss.Condition as JSVariable; if ( (switchVar != null) && IndexLookups.TryGetValue(switchVar, out indexLookup) && Initializers.TryGetValue(indexLookup.Field, out initializer) ) { if (NullChecks.TryGetValue(indexLookup.SwitchVariable, out nullCheck)) { ParentNode.ReplaceChild(nullCheck.Statement, new JSNullStatement()); } Stack.Skip(2).First().ReplaceChildRecursive(initializer.Statement, new JSNullStatement()); if (indexLookup.IsInverted) { ParentNode.ReplaceChild(indexLookup.Statement, new JSNullStatement()); } var switchCases = new List <JSSwitchCase>(); JSExpression[] values; foreach (var cse in ss.Cases) { var body = cse.Body; if (cse.Values == null) { values = null; body = new JSBlockStatement(body.Statements.ToArray()); } else { values = (from v in cse.Values let il = v as JSIntegerLiteral where il != null select initializer.Values[(int)il.Value]).ToArray(); } switchCases.Add(new JSSwitchCase( values, body )); } var newSwitch = new JSSwitchStatement( indexLookup.SwitchVariable, switchCases.ToArray() ); SwitchStatementsDeoptimized += 1; ParentNode.ReplaceChild(ss, newSwitch); var outVar = indexLookup.OutputVariable; foreach (var fn in Stack.OfType <JSFunctionExpression>()) { foreach (var vds in fn.Body.AllChildrenRecursive.OfType <JSVariableDeclarationStatement>().ToArray()) { for (int i = 0, c = vds.Declarations.Count; i < c; i++) { var leftVar = vds.Declarations[i].Left as JSVariable; if ((leftVar != null) && (leftVar.Identifier == outVar.Identifier)) { vds.Declarations.RemoveAt(i); i--; c--; } } } fn.AllVariables.Remove(outVar.Identifier); } VisitReplacement(newSwitch); } VisitChildren(ss); }
public void VisitNode(JSBlockStatement block) { EmitBlockInterior(block.Statements); }