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) { #pragma warning disable 0642 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; #pragma warning restore 0642 } } // 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 block) { SeenRetargetsInScope.Push(new Dictionary<RetargetKey, int>()); ScopeNodeIndices.Push(NodeIndex); VisitChildren(block); SeenRetargetsInScope.Pop(); ScopeNodeIndices.Pop(); }
public JSSwitchCase(JSExpression[] values, JSBlockStatement body) { if ((values != null) && (values.Length == 0)) { values = null; } Values = values; Body = body; }
public JSSwitchCase(JSExpression[] values, JSBlockStatement body, bool isDefault) { if ((values != null) && (values.Length == 0)) { values = null; } Values = values; Body = body; IsDefault = isDefault; }
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 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); }
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) Debug.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; }
internal JSFunctionExpression Create( MethodInfo info, MethodDefinition methodDef, MethodReference method, QualifiedMemberIdentifier identifier, ILBlockTranslator translator, IEnumerable<JSVariable> parameters, JSBlockStatement body ) { var result = new JSFunctionExpression( new JSMethod(method, info), translator.Variables, parameters, body ); var entry = new Entry { Identifier = identifier, Info = info, Reference = method, Expression = result, Variables = translator.Variables, ParameterNames = translator.ParameterNames, SpecialIdentifiers = translator.SpecialIdentifiers }; Cache.Add(identifier, entry); OptimizationQueue.Add(identifier); return result; }
// // IL Node Types // protected JSBlockStatement TranslateBlock(IEnumerable<ILNode> children) { JSBlockStatement result, currentBlock; // TODO: Fix this heuristic by building a flow graph at the beginning of method translation if (children.Any( n => ContainsLabels(n) )) { var index = LabelledBlockCount++; result = new JSLabelGroupStatement(index); currentBlock = new JSBlockStatement(); currentBlock.Label = String.Format("__entry{0}__", index); result.Statements.Add(currentBlock); } else { currentBlock = result = new JSBlockStatement(); } foreach (var node in children) { var label = node as ILLabel; var expr = node as ILExpression; var isGoto = (expr != null) && (expr.Code == ILCode.Br); if (label != null) { currentBlock = new JSBlockStatement { Label = label.Name }; result.Statements.Add(currentBlock); continue; } else if (isGoto) { currentBlock.Statements.Add(new JSExpressionStatement(new JSGotoExpression( ((ILLabel)expr.Operand).Name ))); } else { var translated = TranslateStatement(node); if (translated != null) currentBlock.Statements.Add(translated); } } return result; }
public JSTryCatchBlock TranslateNode(ILTryCatchBlock tcb) { var body = TranslateNode(tcb.TryBlock); JSVariable catchVariable = null; JSBlockStatement catchBlock = null; JSBlockStatement finallyBlock = null; if (tcb.CatchBlocks.Count > 0) { var pairs = new List<KeyValuePair<JSExpression, JSStatement>>(); catchVariable = DeclareVariable(new JSExceptionVariable(TypeSystem, ThisMethodReference)); bool foundUniversalCatch = false; foreach (var cb in tcb.CatchBlocks) { JSExpression pairCondition = null; if ( (cb.ExceptionType.FullName == "System.Exception") || (cb.ExceptionType.FullName == "System.Object") ) { // Bad IL sometimes contains entirely meaningless catch clauses. It's best to just ignore them. if ( (cb.Body.Count == 1) && (cb.Body[0] is ILExpression) && (((ILExpression)cb.Body[0]).Code == ILCode.Rethrow) ) { continue; } if (foundUniversalCatch) { Console.Error.WriteLine("Found multiple catch-all catch clauses. Any after the first will be ignored."); continue; } foundUniversalCatch = true; } else { if (foundUniversalCatch) throw new NotImplementedException("Catch-all clause must be last"); pairCondition = JSIL.CheckType(catchVariable, cb.ExceptionType); } var pairBody = TranslateBlock(cb.Body); if (cb.ExceptionVariable != null) { var excVariable = DeclareVariable(cb.ExceptionVariable, ThisMethodReference); pairBody.Statements.Insert( 0, new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, excVariable, catchVariable, cb.ExceptionVariable.Type )) ); } pairs.Add(new KeyValuePair<JSExpression, JSStatement>( pairCondition, pairBody )); } if (!foundUniversalCatch) pairs.Add(new KeyValuePair<JSExpression,JSStatement>( null, new JSExpressionStatement(new JSThrowExpression(catchVariable)) )); if ((pairs.Count == 1) && (pairs[0].Key == null)) catchBlock = new JSBlockStatement( pairs[0].Value ); else catchBlock = new JSBlockStatement( JSIfStatement.New(pairs.ToArray()) ); } if (tcb.FinallyBlock != null) finallyBlock = TranslateNode(tcb.FinallyBlock); if (tcb.FaultBlock != null) { if (catchBlock != null) throw new Exception("A try block cannot have both a catch block and a fault block"); catchVariable = DeclareVariable(new JSExceptionVariable(TypeSystem, ThisMethodReference)); catchBlock = new JSBlockStatement(TranslateBlock(tcb.FaultBlock.Body)); catchBlock.Statements.Add(new JSExpressionStatement(new JSThrowExpression(catchVariable))); } return new JSTryCatchBlock( body, catchVariable, catchBlock, finallyBlock ); }
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 VisitNode(JSBlockStatement bs) { var thisSwitchCase = ParentSwitchCase; var parentLabelGroup = ParentNode as JSLabelGroupStatement; var isControlFlow = bs.IsControlFlow || (thisSwitchCase != LastSwitchCase) || (parentLabelGroup != null); if (TraceLevel >= 2) Console.WriteLine("// Entering block {0}", bs.Label ?? bs.GetType().Name); if (isControlFlow) { if (TraceLevel >= 2) Console.WriteLine("// Count reset"); AbsoluteJumpsSeenStack.Add(0); } BlockStack.Push(bs); VisitChildren(bs); BlockStack.Pop(); if (TraceLevel >= 2) Console.WriteLine("// Exiting block"); if (isControlFlow) AbsoluteJumpsSeenStack.RemoveAt(AbsoluteJumpsSeenStack.Count - 1); LastSwitchCase = thisSwitchCase; }
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 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"); } } }
protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex, JSBlockStatement enclosingBlock) { var oldDeclaration = statement.Declarations[declarationIndex]; var valueType = oldDeclaration.Right.GetActualType(JSIL.TypeSystem); var newVariable = variable.Reference(); var enclosingFunction = Stack.OfType<JSFunctionExpression>().First(); JSExpression initialValue; // If the declaration was in function scope originally we can hoist the initial value // into our new variable declaration. If not, we need to initialize the ref variable // to the default value for its type. It will get the correct value assigned later. if (enclosingBlock == enclosingFunction.Body) initialValue = oldDeclaration.Right; else initialValue = new JSDefaultValueLiteral(valueType); var newDeclaration = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, // We have to use a constructed ref to the variable here, otherwise // the declaration will look like 'var x.value = foo' new JSVariable(variable.Identifier, variable.IdentifierType, variable.Function), JSIL.NewReference(initialValue), newVariable.IdentifierType )); if (Tracing) Debug.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement)); // Insert the new declaration directly before the top-level block containing the original // declaration. This ensures that if its initial value has a dependency on external state, // the declaration will not precede the values it depends on. // Note that for declarations that were hoisted out of inner blocks (conditionals, loops) // it doesn't actually matter where the insert occurs, since we initialize with a default // value in that case. enclosingFunction.Body.InsertNearChildRecursive( statement, newDeclaration, 0 ); // If the reference is being declared in function scope, it doesn't need a separate assignment // for its initialization. Otherwise, we need to insert an assignment after the original variable // declaration statement to ensure that the reference variable is initialized to the right value // at the exact right point in the function's execution. if (enclosingBlock != enclosingFunction.Body) { var newAssignment = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, newVariable, oldDeclaration.Right, valueType ) ); var insertLocation = enclosingBlock.Statements.IndexOf(statement) + 1; enclosingBlock.Statements.Insert(insertLocation, newAssignment); } Variables[variable.Identifier] = newVariable; statement.Declarations.RemoveAt(declarationIndex); TransformedVariables.Add(variable.Identifier); }
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 JSSwitchCase(JSExpression[] values, JSBlockStatement body, bool isDefault) { if ((values != null) && (values.Length == 0)) values = null; Values = values; Body = body; IsDefault = isDefault; }
public JSSwitchCase(JSExpression[] values, JSBlockStatement body) { if ((values != null) && (values.Length == 0)) values = null; Values = values; Body = body; }
public void VisitNode(JSBlockStatement bs) { CheckLabel(bs); PushBlock(bs); try { VisitChildren(bs); } finally { BlockStack.Pop(); } }
// // IL Node Types // protected JSBlockStatement TranslateBlock(IEnumerable<ILNode> children) { JSBlockStatement result, currentBlock; currentBlock = result = new JSBlockStatement(); foreach (var node in children) { var label = node as ILLabel; var expr = node as ILExpression; var isGoto = (expr != null) && (expr.Code == ILCode.Br); if (label != null) { currentBlock = new JSBlockStatement { Label = label.Name }; result.Statements.Add(currentBlock); continue; } else if (isGoto) { currentBlock.Statements.Add(new JSExpressionStatement(new JSGotoExpression( ((ILLabel)expr.Operand).Name ))); } else { var translated = TranslateStatement(node); if (translated != null) currentBlock.Statements.Add(translated); } } return result; }
public void VisitNode(JSBlockStatement bs) { MaybeHoist(bs, bs.Children.OfType<JSStatement>()); VisitChildren(bs); }
protected void TransformParameterIntoReference(JSVariable parameter, JSBlockStatement block) { var newParameter = new JSParameter("$" + parameter.Identifier, parameter.Type); var newVariable = new JSVariable(parameter.Identifier, new ByReferenceType(parameter.Type)); 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.Type ) ); if (Tracing) Debug.WriteLine(String.Format("Transformed {0} into {1}={2}", parameter, newVariable, newParameter)); Variables[newVariable.Identifier] = newVariable; Variables.Add(newParameter.Identifier, newParameter); ParameterNames.Remove(parameter.Identifier); ParameterNames.Add(newParameter.Identifier); block.Statements.Insert(0, newDeclaration); }