public void VisitNode (JSExpressionStatement es) { var invocationExpression = es.Expression as JSInvocationExpression; if (invocationExpression != null) { var replacement = MaybeReplaceInvocation(invocationExpression); if (replacement != null) { ParentNode.ReplaceChild(es, replacement); VisitReplacement(replacement); return; } } VisitChildren(es); }
public void BuildLabelGroups() { foreach (var g in Gotos) { var targetLabel = Labels[g.TargetLabel]; if (targetLabel.EnclosingBlock.Depth > g.EnclosingBlock.Depth) targetLabel.EnclosingBlock = g.EnclosingBlock; } foreach (var l in Labels.Values) { l.EnsureLabelGroupExists(LabelGroups); var replacementGoto = new JSExpressionStatement( new JSGotoExpression(l.LabelledStatement.Label) ); l.EnclosingBlock.Block.ReplaceChildRecursive(l.LabelledStatement, replacementGoto); l.LabelGroup.Add(l.LabelledStatement); } }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (ToDeclare.Count > 0) { int i = 0; foreach (var pd in ToDeclare) { var es = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, pd.Expression, new JSDefaultValueLiteral(pd.Type), pd.Type )); fn.Body.Statements.Insert(i++, es); } } }
public void BuildLabelGroups(JSFunctionExpression function) { // If a label is applied to the first statement in a block, hoist it upward // onto the parent block. var lh = new LabelHoister(); do { lh.HoistedALabel = false; lh.Visit(function); } while (lh.HoistedALabel); // Walk the function to build our list of labels and gotos. Visit(function); // When a goto crosses block boundaries, we need to move the target label // upwards so that the goto can reach it. foreach (var g in Gotos) { var targetLabel = Labels[g.TargetLabel]; if (targetLabel.EnclosingBlock.Depth > g.EnclosingBlock.Depth) targetLabel.EnclosingBlock = g.EnclosingBlock; } foreach (var l in Labels.Values) { l.EnsureLabelGroupExists(LabelGroups); var replacementGoto = new JSExpressionStatement( new JSGotoExpression(l.LabelledStatement.Label) ); l.EnclosingBlock.Block.ReplaceChildRecursive(l.LabelledStatement, replacementGoto); l.LabelGroup.Add(l.LabelledStatement); } // If a label group only contains one label (plus an entry label), // and it has a parent label group, hoist the label up. var lgs = new LabelGroupFlattener(); do { lgs.FlattenedAGroup = false; lgs.Visit(function); } while (lgs.FlattenedAGroup); // Remove any labels within a label group that contain no statements (as long // as no goto targets that label directly). This will prune empty entry/exit labels. var elr = new EmptyLabelRemover(UsedLabels); elr.Visit(function); }
protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex, JSBlockStatement enclosingBlock) { var oldDeclaration = statement.Declarations[declarationIndex]; var valueType = oldDeclaration.Right.GetActualType(JSIL.TypeSystem); var newVariable = variable.Reference(); var enclosingFunction = Stack.OfType<JSFunctionExpression>().First(); JSExpression initialValue; // If the declaration was in function scope originally we can hoist the initial value // into our new variable declaration. If not, we need to initialize the ref variable // to the default value for its type. It will get the correct value assigned later. if (enclosingBlock == enclosingFunction.Body) initialValue = oldDeclaration.Right; else initialValue = new JSDefaultValueLiteral(valueType); var newDeclaration = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, // We have to use a constructed ref to the variable here, otherwise // the declaration will look like 'var x.value = foo' new JSVariable(variable.Identifier, variable.IdentifierType, variable.Function), JSIL.NewReference(initialValue), newVariable.IdentifierType )); if (Tracing) Debug.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement)); // Insert the new declaration directly before the top-level block containing the original // declaration. This ensures that if its initial value has a dependency on external state, // the declaration will not precede the values it depends on. // Note that for declarations that were hoisted out of inner blocks (conditionals, loops) // it doesn't actually matter where the insert occurs, since we initialize with a default // value in that case. enclosingFunction.Body.InsertNearChildRecursive( statement, newDeclaration, 0 ); // If the reference is being declared in function scope, it doesn't need a separate assignment // for its initialization. Otherwise, we need to insert an assignment after the original variable // declaration statement to ensure that the reference variable is initialized to the right value // at the exact right point in the function's execution. if (enclosingBlock != enclosingFunction.Body) { var newAssignment = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, newVariable, oldDeclaration.Right, valueType ) ); var insertLocation = enclosingBlock.Statements.IndexOf(statement) + 1; enclosingBlock.Statements.Insert(insertLocation, newAssignment); } Variables[variable.Identifier] = newVariable; statement.Declarations.RemoveAt(declarationIndex); TransformedVariables.Add(variable.Identifier); }
protected JSStatement TranslateStatement(ILNode node) { var translated = TranslateNode(node as dynamic); var statement = translated as JSStatement; if (statement == null) { var expression = (JSExpression)translated; if (expression != null) statement = new JSExpressionStatement(expression); else Console.Error.WriteLine("Warning: Null statement: {0}", node); } return statement; }
public void VisitNode(JSExpressionStatement node) { VisitChildren(node); }
protected void TranslateTypeStaticConstructor(DecompilerContext context, JavascriptFormatter output, TypeDefinition typedef, MethodDefinition cctor, bool stubbed) { var typeSystem = context.CurrentModule.TypeSystem; var fieldsToEmit = (from f in typedef.Fields where f.IsStatic && NeedsStaticConstructor(f.FieldType) select f).ToArray(); // We initialize all static fields in the cctor to avoid ordering issues Action<JSFunctionExpression> fixupCctor = (f) => { int insertPosition = 0; foreach (var field in fieldsToEmit) { var expr = TranslateField(field); if (expr != null) { var stmt = new JSExpressionStatement(expr); f.Body.Statements.Insert(insertPosition++, stmt); } } }; // Default values for instance fields of struct types are handled // by the instance constructor. // Default values for static fields of struct types are handled // by the cctor. // Everything else is emitted inline. foreach (var f in typedef.Fields) { if (f.IsStatic && NeedsStaticConstructor(f.FieldType)) continue; if (EmulateStructAssignment.IsStruct(f.FieldType)) continue; var expr = TranslateField(f); if (expr != null) AstEmitter.Visit(new JSExpressionStatement(expr)); } if ((cctor != null) && !stubbed) { TranslateMethod(context, output, cctor, cctor, false, null, null, fixupCctor); } else if (fieldsToEmit.Length > 0) { var fakeCctor = new MethodDefinition(".cctor", Mono.Cecil.MethodAttributes.Static, typeSystem.Void); fakeCctor.DeclaringType = typedef; var typeInfo = TypeInfoProvider.GetTypeInformation(typedef); typeInfo.StaticConstructor = fakeCctor; var identifier = MemberIdentifier.New(fakeCctor); typeInfo.Members[identifier] = new Internal.MethodInfo( typeInfo, identifier, fakeCctor, new ProxyInfo[0], false ); // Generate the fake constructor, since it wasn't created during the analysis pass TranslateMethodExpression(context, fakeCctor, fakeCctor); TranslateMethod(context, output, fakeCctor, fakeCctor, false, null, null, fixupCctor); } }