public AstActionBlock(ISourcePosition position, AstBlock parent, AstAction action) : base(position,parent) { if (action == null) throw new ArgumentNullException("action"); Action = action; }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstBlock ast) { var variableExpressions = ast.GetExpressions().Where(x => x.Operand is AstVariable).ToList(); var allVariables = variableExpressions.Select(x => (AstVariable)x.Operand).Distinct().ToList(); var allStructVariables = allVariables.Where(x => x.Type.IsStruct()).ToList(); var index = 0; foreach (var iterator in allStructVariables) { var variable = iterator; if (variable.IsThis || variable.IsParameter) continue; if (variableExpressions.Any(x => (x.Operand == variable) && (x.Code == AstCode.Stloc))) continue; // Get struct type var structType = variable.Type.Resolve(); var defaultCtorDef = StructCallConverter.GetDefaultValueCtor(structType); var defaultCtor = variable.Type.CreateReference(defaultCtorDef); // Variable is not initialized var newObjExpr = new AstExpression(ast.SourceLocation, AstCode.Newobj, defaultCtor).SetType(variable.Type); var initExpr = new AstExpression(ast.SourceLocation, AstCode.Stloc, variable, newObjExpr).SetType(variable.Type); ast.Body.Insert(index++, initExpr); } }
public void ShouldBeAppliedIfFunctionDoesNotEndWithReturn() { var block = new AstBlock(null); var lambdaAst = new AstParameterlessLambda(block, null); block.Expressions.Add( new AstFunctionCall("println", new List <AstExpression> { new AstBinaryMathOperation(BinaryMath.Plus, new AstIntegerConstant(5, null), new AstIntegerConstant(5, null), null) }, null)); block.Expressions.Add( new AstFunctionCall("println", new List <AstExpression> { new AstStringConstant("hello world", null) }, null) ); var env = new ScriptEnvironment(new OperationCodeFactory(), new ValueFactory()); Compiler.Compile(env, new AstRoot(new Position(0, 0)) { Expressions = { lambdaAst } }); Assert.Equal(typeof(Return), env.Functions[GetGlobalName(Compiler.GetAnonymousFunctionName(1))].Code.Last().Op.GetType()); }
/// <summary> /// Create the body of the valueOf(string) method. /// </summary> private AstBlock CreateValueOfBody(XSyntheticMethodDefinition method, XTypeSystem typeSystem) { var fields = XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition)).ToList(); var ast = AstBlock.Create <AstExpression>(); // Find name foreach (var field in fields) { var notEqualLabel = new AstLabel(AstNode.NoSource, "not_equal_to" + field.Name); var equalsExpr = new AstExpression(AstNode.NoSource, AstCode.Call, FrameworkReferences.StringEquals(typeSystem), new AstExpression(AstNode.NoSource, AstCode.Ldstr, field.Name), new AstExpression(AstNode.NoSource, AstCode.Ldloc, method.AstParameters[0])); // If !equals(name, field.name) goto notEqualLabel ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Brfalse, notEqualLabel, equalsExpr)); // Return field object ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, field))); // notEqualLabel: ast.Body.Add(notEqualLabel); } // Return null ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.Ldnull, null))); return(ast); }
/// <summary> /// Create the body of the unbox(object) method. /// </summary> private AstBlock CreateUnboxBody(XSyntheticMethodDefinition method, bool isWide, AssemblyCompiler compiler) { // Prepare var loc = AstNode.NoSource; Func <AstExpression> ldValue = () => new AstExpression(loc, AstCode.Ldloc, method.AstParameters[0]); var afterMyEnum = new AstLabel(loc, "_afterMyEnum"); // value instanceof MyEnumType? var ifNotInstanceOfMyEnum = new AstExpression(loc, AstCode.Brfalse, afterMyEnum, new AstExpression(loc, AstCode.SimpleInstanceOf, XType, ldValue())); var returnMyEnum = new AstExpression(loc, AstCode.Ret, null, new AstExpression(loc, AstCode.SimpleCastclass, XType, ldValue())); // boxToMyEnum(UnboxInteger(value)) var boxingType = compiler.GetDot42InternalType("Boxing").Resolve(); var unboxNumericMethod = boxingType.Methods.First(x => x.Name == (isWide ? "UnboxLong" : "UnboxInteger")); var unboxToNumeric = new AstExpression(loc, AstCode.Call, unboxNumericMethod, ldValue()); var numericToMyEnum = new AstExpression(loc, isWide ? AstCode.Long_to_enum : AstCode.Int_to_enum, XType, unboxToNumeric).SetType(XType); var returnX = new AstExpression(loc, AstCode.Ret, null, numericToMyEnum); var ast = new AstBlock(loc, new AstNode[] { ifNotInstanceOfMyEnum, returnMyEnum, afterMyEnum, returnX }); return(ast); }
private static void FixBlock(AstBlock block) { if (block == null) return; bool wasLastExpressionCallToMonitorEnter = false; foreach (AstNode node in block.GetChildren()) { var expr = node as AstExpression; if (expr != null) { if(expr.Code != AstCode.Nop) wasLastExpressionCallToMonitorEnter = IsCallToMonitorEnter(expr); continue; } var tryCatch = node as AstTryCatchBlock; if (wasLastExpressionCallToMonitorEnter && tryCatch != null) { // a lock statement. FixTryCatchFinally(tryCatch); } wasLastExpressionCallToMonitorEnter = false; } }
private static void FixBlock(AstBlock block) { if (block == null) { return; } bool wasLastExpressionCallToMonitorEnter = false; foreach (AstNode node in block.GetChildren()) { var expr = node as AstExpression; if (expr != null) { if (expr.Code != AstCode.Nop) { wasLastExpressionCallToMonitorEnter = IsCallToMonitorEnter(expr); } continue; } var tryCatch = node as AstTryCatchBlock; if (wasLastExpressionCallToMonitorEnter && tryCatch != null) { // a lock statement. FixTryCatchFinally(tryCatch); } wasLastExpressionCallToMonitorEnter = false; } }
/// <summary> /// Is the given field initialized in the given code? /// </summary> private static bool IsInitialized(AstBlock ast, XFieldDefinition field) { var storeCode = field.IsStatic ? AstCode.Stsfld : AstCode.Stfld; var initExpressions = ast.GetSelfAndChildrenRecursive <AstExpression>(x => (x.Code == storeCode) && ((XFieldReference)x.Operand).IsSame(field)).ToList(); return(initExpressions.Any()); }
/// <summary> /// Optimize expressions /// </summary> public static void Convert(AstBlock ast) { var variableExpressions = ast.GetExpressions().Where(x => x.Operand is AstVariable).ToList(); var allVariables = variableExpressions.Select(x => (AstVariable)x.Operand).Distinct().ToList(); var allStructVariables = allVariables.Where(x => x.Type.IsStruct()).ToList(); var index = 0; foreach (var iterator in allStructVariables) { var variable = iterator; if (variable.IsThis || variable.IsParameter) { continue; } if (variableExpressions.Any(x => (x.Operand == variable) && (x.Code == AstCode.Stloc))) { continue; } // Get struct type var structType = variable.Type.Resolve(); var defaultCtorDef = StructCallConverter.GetDefaultValueCtor(structType); var defaultCtor = variable.Type.CreateReference(defaultCtorDef); // Variable is not initialized var newObjExpr = new AstExpression(ast.SourceLocation, AstCode.Newobj, defaultCtor).SetType(variable.Type); var initExpr = new AstExpression(ast.SourceLocation, AstCode.Stloc, variable, newObjExpr).SetType(variable.Type); ast.Body.Insert(index++, initExpr); } }
public AstScopedBlock([NotNull] ISourcePosition p, [NotNull] AstBlock lexicalScope, string uid = null, string prefix = null) : base(p,lexicalScope, uid:uid, prefix:prefix) { if (lexicalScope == null) throw new System.ArgumentNullException("lexicalScope"); _lexicalScope = lexicalScope; }
public AstWhileLoop(ISourcePosition position, AstBlock parentBlock, bool isPrecondition = true, bool isPositive = true) : base(position,parentBlock) { IsPrecondition = isPrecondition; IsPositive = isPositive; }
void ApplyLambdaBlock(AstBlock ast, DrawBlock parent, Block result) { if (ast.UsingBlocks == null) { var usingBlocks = new List <Block>(); for (int j = 0, l = result.Members.Count; j < l; j++) { var apply = result.Members[j] as Apply; if (apply != null) { usingBlocks.Add(apply.Block); } } result.SetUsingBlocks(usingBlocks.ToArray()); } Expression obj = null; if (!parent.Method.IsStatic) { obj = new This(parent.Source, _compiler.TypeBuilder.Parameterize(parent.Method.DeclaringType)).Address; } parent.Members.Add(new Apply(ast.Name.Source, 0, result, obj)); }
/// <summary> /// AST ctor /// </summary> public MethodSource(XMethodDefinition method, AstBlock ast) { if (ast == null) throw new ArgumentNullException(); this.method = method; this.ast = ast; }
public static void Run(DecompilerContext context, AstBlock method) { var ta = new TypeAnalysis(context); ta.CreateDependencyGraph(method); ta.IdentifySingleLoadVariables(); ta.RunInference(); }
public bool InlineAllInBlock(AstBlock block) { bool modified = false; List<AstNode> body = block.Body; if (block is AstTryCatchBlock.CatchBlock && body.Count > 1) { AstVariable v = ((AstTryCatchBlock.CatchBlock)block).ExceptionVariable; if (v != null && v.IsGenerated) { if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) { AstVariable v2; AstExpression ldException; if (body[0].Match(AstCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) { body.RemoveAt(0); ((AstTryCatchBlock.CatchBlock)block).ExceptionVariable = v2; modified = true; } } } } for(int i = 0; i < body.Count - 1;) { AstVariable locVar; AstExpression expr; if (body[i].Match(AstCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) { modified = true; i = Math.Max(0, i - 1); // Go back one step } else { i++; } } foreach(AstBasicBlock bb in body.OfType<AstBasicBlock>()) { modified |= InlineAllInBasicBlock(bb); } return modified; }
public AstLoopBlock(string file, int line, int column, AstBlock parentBlock, string uid = null, string prefix = null) : this (new SourcePosition(file,line,column), parentBlock, uid, prefix) { }
private static void Generate(CodeGenContext context, AstBlock block) { AstBlockVisitor visitor = new AstBlockVisitor(block); var loopTargets = new Dictionary <AstBlock, (SpvInstruction, SpvInstruction)>(); visitor.BlockEntered += (sender, e) => { AstBlock mergeBlock = e.Block.Parent; if (e.Block.Type == AstBlockType.If) { AstBlock ifTrueBlock = e.Block; AstBlock ifFalseBlock; if (AstHelper.Next(e.Block) is AstBlock nextBlock && nextBlock.Type == AstBlockType.Else) { ifFalseBlock = nextBlock; } else { ifFalseBlock = mergeBlock; } var condition = context.Get(AggregateType.Bool, e.Block.Condition); context.SelectionMerge(context.GetNextLabel(mergeBlock), SelectionControlMask.MaskNone); context.BranchConditional(condition, context.GetNextLabel(ifTrueBlock), context.GetNextLabel(ifFalseBlock)); }
public void RemoveGotos(AstBlock method) { // Build the navigation data parent[method] = null; foreach (var node in method.GetSelfAndChildrenRecursive<AstNode>()) { AstNode previousChild = null; foreach (var child in node.GetChildren()) { if (parent.ContainsKey(child)) throw new Exception("The following expression is linked from several locations: " + child); parent[child] = node; if (previousChild != null) nextSibling[previousChild] = child; previousChild = child; } if (previousChild != null) nextSibling[previousChild] = null; } // Simplify gotos bool modified; do { modified = false; foreach (var gotoExpr in method.GetSelfAndChildrenRecursive<AstExpression>(e => e.Code == AstCode.Br || e.Code == AstCode.Leave)) { modified |= TrySimplifyGoto(gotoExpr); } } while(modified); RemoveRedundantCode(method); }
/// <summary> /// Add generic instance field initialization code. /// </summary> private static void AddGenericInstanceFieldInitializationCode(AstBlock ast) { var initExpr = new AstExpression(ast.SourceLocation, AstCode.StGenericInstanceField, null, new AstExpression(ast.SourceLocation, AstCode.LdGenericInstanceTypeArgument, null)); InsertAfter(ast, null, new[] { initExpr }); }
public AstTryCatchFinally(ISourcePosition p, AstBlock lexicalScope) : base(p, lexicalScope) { TryBlock = new AstScopedBlock(p, this); CatchBlock = new AstScopedBlock(p, TryBlock); FinallyBlock = new AstScopedBlock(p, TryBlock); }
protected AstScopedExpr([NotNull] ISourcePosition position, [NotNull] AstBlock lexicalScope) : base(position) { if (lexicalScope == null) throw new System.ArgumentNullException("lexicalScope"); _lexicalScope = lexicalScope; }
public override AstNode ShallowClone() { var res = new AstBlock(Source, Start, End); res.Body.AddRange(Body.AsReadOnlySpan()); return(res); }
protected AstScopedExpr([NotNull] string file, int line, int column, [NotNull] AstBlock lexicalScope) : base(file, line, column) { if (lexicalScope == null) throw new System.ArgumentNullException("lexicalScope"); _lexicalScope = lexicalScope; }
internal AstScopedExpr([NotNull] Parser p, [NotNull] AstBlock lexicalScope) : base(p) { if (lexicalScope == null) throw new System.ArgumentNullException("lexicalScope"); _lexicalScope = lexicalScope; }
/// <summary> /// Create the body of the values() method. /// </summary> private AstBlock CreateValuesBody() { var fields = XType.Fields.Where(x => x.IsStatic && !(x is XSyntheticFieldDefinition)).ToList(); // Allocate array var arrVar = new AstGeneratedVariable("array", "array") { Type = new XArrayType(XType) }; var createArr = new AstExpression(AstNode.NoSource, AstCode.Stloc, arrVar, new AstExpression(AstNode.NoSource, AstCode.Newarr, XType, new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, fields.Count))); var ast = AstBlock.CreateOptimizedForTarget(createArr); // Initialize array var index = 0; foreach (var field in fields) { ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Stelem_Any, null, new AstExpression(AstNode.NoSource, AstCode.Ldloc, arrVar), new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, index), new AstExpression(AstNode.NoSource, AstCode.Ldsfld, field))); index++; } // Return array ast.Body.Add(new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.Ldloc, arrVar))); return(ast); }
/// <summary> /// Group input into a set of blocks that can be later arbitraliby schufled. /// The method adds necessary branches to make control flow between blocks /// explicit and thus order independent. /// </summary> private void SplitToBasicBlocks(AstBlock block) { var basicBlocks = new List <AstNode>(); var entryLabel = block.Body.FirstOrDefault() as AstLabel ?? new AstLabel(block.SourceLocation, "Block_" + (nextLabelIndex++)); var basicBlock = new AstBasicBlock(block.SourceLocation); basicBlocks.Add(basicBlock); basicBlock.Body.Add(entryLabel); block.EntryGoto = new AstExpression(block.SourceLocation, AstCode.Br, entryLabel); if (block.Body.Count > 0) { if (block.Body[0] != entryLabel) { basicBlock.Body.Add(block.Body[0]); } for (var i = 1; i < block.Body.Count; i++) { var lastNode = block.Body[i - 1]; var currNode = block.Body[i]; // Start a new basic block if necessary if (currNode is AstLabel || currNode is AstTryCatchBlock || // Counts as label lastNode.IsConditionalControlFlow() || lastNode.IsUnconditionalControlFlow()) { // Try to reuse the label var label = currNode as AstLabel ?? new AstLabel(currNode.SourceLocation, "Block_" + (nextLabelIndex++)); // Terminate the last block if (!lastNode.IsUnconditionalControlFlow()) { // Explicit branch from one block to other basicBlock.Body.Add(new AstExpression(lastNode.SourceLocation, AstCode.Br, label)); } // Start the new block basicBlock = new AstBasicBlock(currNode.SourceLocation); basicBlocks.Add(basicBlock); basicBlock.Body.Add(label); // Add the node to the basic block if (currNode != label) { basicBlock.Body.Add(currNode); } } else { basicBlock.Body.Add(currNode); } } } block.Body = basicBlocks; }
/// <summary> /// Convert the given method into optimized Ast format. /// </summary> internal protected static AstNode CreateOptimizedAst(AssemblyCompiler compiler, MethodSource source, bool generateSetNextInstructionCode, StopAstConversion debugStop = StopAstConversion.None, AstOptimizationStep debugStopOptimizing = AstOptimizationStep.None ) { // Build AST DecompilerContext context; AstBlock ast; if (source.IsDotNet) { context = new DecompilerContext(source.Method); var astBuilder = new IL2Ast.AstBuilder(source.ILMethod, true, context); var children = astBuilder.Build(); ast = new AstBlock(children.Select(x => x.SourceLocation).FirstOrDefault(), children); if ((source.ILMethod.IsConstructor) && (source.Method.DeclaringType.Fields.Any(x => x.FieldType.IsEnum() || x.Name.EndsWith(NameConstants.Atomic.FieldUpdaterPostfix)))) { // Ensure all fields are initialized AddFieldInitializationCode(compiler, source, ast); } if (source.Method.NeedsGenericInstanceTypeParameter && (source.Name == ".ctor")) { // Add code to save the generic instance type parameter into the generic instance field. AddGenericInstanceFieldInitializationCode(source, ast, compiler.Module.TypeSystem); } } else if (source.IsJava) { var astBuilder = new Java2Ast.AstBuilder(compiler.Module, source.JavaMethod, source.Method.DeclaringType, true); context = new DecompilerContext(source.Method); ast = astBuilder.Build(); } else if (source.IsAst) { context = new DecompilerContext(source.Method); ast = source.Ast; } else { throw new NotSupportedException("Unknown source"); } if (debugStop == StopAstConversion.AfterILConversion) return ast; // Optimize AST var astOptimizer = new AstOptimizer(context, ast); astOptimizer.Optimize(debugStopOptimizing); if (debugStop == StopAstConversion.AfterOptimizing) return ast; // Optimize AST towards the target TargetConverters.Convert(context, ast, source, compiler, debugStop); if(generateSetNextInstructionCode) SetNextInstructionGenerator.Convert(ast, source, compiler); // Return return return ast; }
/// <summary> /// Create the body of the ctor. /// </summary> private AstBlock CreateCtorBody() { return(AstBlock.CreateOptimizedForTarget( // Call base ctor new AstExpression(AstNode.NoSource, AstCode.CallBaseCtor, 0, new AstExpression(AstNode.NoSource, AstCode.Ldthis, null)), // Return new AstExpression(AstNode.NoSource, AstCode.Ret, null))); }
/// <summary> /// Create the body of the Create(int|long) method. /// </summary> private AstBlock CreateCreateBody(XSyntheticMethodDefinition method, XMethodReference ctor) { return(AstBlock.CreateOptimizedForTarget( new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.Newobj, ctor, new AstExpression(AstNode.NoSource, AstCode.Ldstr, "?"), new AstExpression(AstNode.NoSource, AstCode.Ldc_I4, -1), new AstExpression(AstNode.NoSource, AstCode.Ldloc, method.AstParameters[0]))))); }
public AstForLoop(ISourcePosition position, AstBlock parentBlock) : this(position, new AstScopedBlock( position, new AstScopedBlock( position, parentBlock, prefix: "init"), prefix:"next")) { }
/// <summary> /// AST ctor /// </summary> public MethodSource(XMethodDefinition method, AstBlock ast) { if (ast == null) { throw new ArgumentNullException(); } this.method = method; this.ast = ast; }
public VariableDefinition(AstBlock parentBlock, AstNode parent, AstVar astVar, AstVarDef astVarDef, bool canMoveInitialization, int originalIndexInVar) { ParentBlock = parentBlock; Parent = parent; AstVar = astVar; CanMoveInitialization = canMoveInitialization; OriginalIndexInVar = originalIndexInVar; AstVarDef = astVarDef; }
internal sealed override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { string debugString = (IsSingletonDeclaration) ? "SINGLETON" : ((this is ClassDefinition) ? "CLASS" : "MODULE") + " " + QualifiedName.Name; ScopeBuilder outerLocals = gen.CurrentScope; // definition needs to take place outside the defined lexical scope: var definition = MakeDefinitionExpression(gen); var selfVariable = outerLocals.DefineHiddenVariable("#module", typeof(RubyModule)); var parentScope = gen.CurrentScopeVariable; // inner locals: ScopeBuilder scope = DefineLocals(); var scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyScope)); gen.EnterModuleDefinition( scope, selfVariable, scopeVariable, IsSingletonDeclaration ); // transform body: MSA.Expression transformedBody = Body.TransformRead(gen); // outer local: MSA.Expression resultVariable = outerLocals.DefineHiddenVariable("#result", transformedBody.Type); // begin with new scope // self = DefineModule/Class(... parent scope here ...) // <body> // end MSA.Expression result = new AstBlock { gen.DebugMarker(debugString), Ast.Assign(selfVariable, definition), scope.CreateScope( scopeVariable, Methods.CreateModuleScope.OpCall( scope.MakeLocalsStorage(), scope.GetVariableNamesExpression(), parentScope, selfVariable ), Ast.Block( Ast.Assign(resultVariable, transformedBody), AstUtils.Empty() ) ), gen.DebugMarker("END OF " + debugString), resultVariable }; gen.LeaveModuleDefinition(); return(result); }
bool BlockIsDisallowed(AstBlock node) { return(node is AstSwitch || node is AstSwitchBranch || node is AstLambda || node is AstTry || node is AstCatch || node is AstFinally || node is AstClass); }
public AstCondition(ISourcePosition p, AstBlock parentBlock, AstExpr condition, bool isNegative = false) : base(p) { IfBlock = new AstScopedBlock(p,parentBlock,prefix: "if"); ElseBlock = new AstScopedBlock(p,parentBlock,prefix:"else"); if (condition == null) throw new ArgumentNullException("condition"); Condition = condition; IsNegative = isNegative; }
/// <summary> /// Create the body of the IntValue or LongValue method. /// </summary> private AstBlock CreateIntOrLongValueBody() { // Get value__ var valueField = XType.Fields.First(x => !x.IsStatic && x.Name == NameConstants.Enum.ValueFieldName); return(AstBlock.CreateOptimizedForTarget( new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.Ldfld, valueField, new AstExpression(AstNode.NoSource, AstCode.Ldthis, null))))); }
public static void RemoveRedundantCode(AstBlock method) { // Remove dead lables and nops HashSet<AstLabel> liveLabels = new HashSet<AstLabel>(method.GetSelfAndChildrenRecursive<AstExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())); foreach(AstBlock block in method.GetSelfAndChildrenRecursive<AstBlock>()) { block.Body = block.Body.Where(n => !n.Match(AstCode.Nop) && !(n is AstLabel && !liveLabels.Contains((AstLabel)n))).ToList(); } // Remove redundant break at the end of case // Remove redundant case blocks altogether foreach(AstSwitch ilSwitch in method.GetSelfAndChildrenRecursive<AstSwitch>()) { foreach(AstBlock ilCase in ilSwitch.CaseBlocks) { Debug.Assert(ilCase.EntryGoto == null); int count = ilCase.Body.Count; if (count >= 2) { if (ilCase.Body[count - 2].IsUnconditionalControlFlow() && ilCase.Body[count - 1].Match(AstCode.LoopOrSwitchBreak)) { ilCase.Body.RemoveAt(count - 1); } } } var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null); // If there is no default block, remove empty case blocks if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(AstCode.LoopOrSwitchBreak))) { ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(AstCode.LoopOrSwitchBreak)); } } // Remove redundant return at the end of method if (method.Body.Count > 0 && method.Body.Last().Match(AstCode.Ret) && ((AstExpression)method.Body.Last()).Arguments.Count == 0) { method.Body.RemoveAt(method.Body.Count - 1); } // Remove unreachable return statements bool modified = false; foreach(AstBlock block in method.GetSelfAndChildrenRecursive<AstBlock>()) { for (int i = 0; i < block.Body.Count - 1;) { if (block.Body[i].IsUnconditionalControlFlow() && block.Body[i+1].Match(AstCode.Ret)) { modified = true; block.Body.RemoveAt(i+1); } else { i++; } } } if (modified) { // More removals might be possible new GotoRemoval().RemoveGotos(method); } }
/// <summary> /// Insert all expressions after the given marker. /// </summary> private static void InsertAfter(AstBlock ast, AstExpression marker, IEnumerable <AstExpression> expressions) { if (marker != null) { var block = ast.GetSelfAndChildrenRecursive <AstBlock>(b => b.Body.Contains(marker)).First(); var index = block.Body.IndexOf(marker); block.Body.InsertRange(index + 1, expressions); } else { ast.Body.InsertRange(0, expressions); } }
/// <summary> /// Create the body of the values() method. /// </summary> private AstBlock CreateValuesBody(XFieldDefinition enumInfoField, XTypeSystem typeSystem) { var internalEnumInfoType = Compiler.GetDot42InternalType("EnumInfo"); var valuesMethod = new XMethodReference.Simple("Values", true, typeSystem.Object, internalEnumInfoType); var ast = AstBlock.CreateOptimizedForTarget( new AstExpression(AstNode.NoSource, AstCode.Ret, null, new AstExpression(AstNode.NoSource, AstCode.SimpleCastclass, new XArrayType(XType), new AstExpression(AstNode.NoSource, AstCode.Call, valuesMethod, new AstExpression(AstNode.NoSource, AstCode.Ldsfld, enumInfoField))))); return(ast); }
/// <summary> /// Creates a new macro context in the specified macro session. /// </summary> /// <param name = "session">The macro expansion session.</param> /// <param name = "invocation">The node that is being expanded.</param> /// <param name = "isJustEffect">Whether the nodes return value will be discarded by the surrounding program.</param> internal MacroContext(MacroSession session, AstGetSet invocation, bool isJustEffect) { if (session == null) throw new ArgumentNullException("session"); if (invocation == null) throw new ArgumentNullException("invocation"); _isJustEffect = isJustEffect; _invocation = invocation; _session = session; _block = new AstScopedBlock(invocation.Position, session.CurrentBlock); _isPartialApplication = _invocation.Arguments.Any(AstPartiallyApplicable.IsPlaceholder); _sentinelBlock = session.Target.CurrentBlock; }
/// <summary> /// Create the body of the ctor. /// </summary> private AstBlock CreateCtorBody() { // here we could also preserve the original underlying type. bool isWide = Type.GetEnumUnderlyingType().Resolve().IsWide(); var underlying = isWide ? Compiler.Module.TypeSystem.Long : Compiler.Module.TypeSystem.Int; return(AstBlock.CreateOptimizedForTarget( // Call base ctor new AstExpression(AstNode.NoSource, AstCode.CallBaseCtor, 0, new AstExpression(AstNode.NoSource, AstCode.Ldthis, null), new AstExpression(AstNode.NoSource, AstCode.TypeOf, underlying)), // Return new AstExpression(AstNode.NoSource, AstCode.Ret, null))); }
/// <summary> /// Perform one pass of a given optimization on this block. /// This block must consist of only basicblocks. /// </summary> public static bool RunOptimization(this AstBlock block, Func <List <AstNode>, AstBasicBlock, int, bool> optimization) { bool modified = false; List <AstNode> body = block.Body; for (int i = body.Count - 1; i >= 0; i--) { if (i < body.Count && optimization(body, (AstBasicBlock)body[i], i)) { modified = true; } } return(modified); }
/// <summary> /// Convert the given method into optimized Ast format. /// </summary> protected static AstNode CreateOptimizedAst(AssemblyCompiler compiler, MethodSource source) { // Build AST DecompilerContext context; AstBlock ast; if (source.IsDotNet) { context = new DecompilerContext(source.Method); var astBuilder = new IL2Ast.AstBuilder(source.ILMethod, true, context); var children = astBuilder.Build(); ast = new AstBlock(children.Select(x => x.SourceLocation).FirstOrDefault(), children); if ((source.ILMethod.IsConstructor) && (source.Method.DeclaringType.Fields.Any(x => x.FieldType.IsEnum()))) { // Ensure all fields are initialized AddFieldInitializationCode(source, ast); } if (source.Method.NeedsGenericInstanceTypeParameter && (source.Name == ".ctor")) { // Add code to safe the generic instance type parameter into the generic instance field. AddGenericInstanceFieldInitializationCode(ast); } } else if (source.IsJava) { var astBuilder = new Java2Ast.AstBuilder(compiler.Module, source.JavaMethod, source.Method.DeclaringType, true); context = new DecompilerContext(source.Method); ast = astBuilder.Build(); } else if (source.IsAst) { context = new DecompilerContext(source.Method); ast = source.Ast; } else { throw new NotSupportedException("Unknown source"); } // Optimize AST var astOptimizer = new AstOptimizer(context, ast); astOptimizer.Optimize(); // Optimize AST towards the target TargetConverters.Convert(context, ast, source, compiler); // Return return return(ast); }
public SimpleControlFlow(DecompilerContext context, AstBlock method) { this.context = context; this.typeSystem = context.CurrentModule.TypeSystem; foreach(AstLabel target in method.GetSelfAndChildrenRecursive<AstExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; } foreach(AstBasicBlock bb in method.GetSelfAndChildrenRecursive<AstBasicBlock>()) { foreach(AstLabel label in bb.GetChildren().OfType<AstLabel>()) { labelToBasicBlock[label] = bb; } } }
/// <summary> /// Convert the given method into optimized Ast format. /// </summary> protected static AstNode CreateOptimizedAst(AssemblyCompiler compiler, MethodSource source) { // Build AST DecompilerContext context; AstBlock ast; if (source.IsDotNet) { context = new DecompilerContext(source.Method); var astBuilder = new IL2Ast.AstBuilder(source.ILMethod, true, context); var children = astBuilder.Build(); ast = new AstBlock(children.Select(x => x.SourceLocation).FirstOrDefault(), children); if ((source.ILMethod.IsConstructor) && (source.Method.DeclaringType.Fields.Any(x => x.FieldType.IsEnum()))) { // Ensure all fields are initialized AddFieldInitializationCode(source, ast); } if (source.Method.NeedsGenericInstanceTypeParameter && (source.Name == ".ctor")) { // Add code to safe the generic instance type parameter into the generic instance field. AddGenericInstanceFieldInitializationCode(ast); } } else if (source.IsJava) { var astBuilder = new Java2Ast.AstBuilder(compiler.Module, source.JavaMethod, source.Method.DeclaringType, true); context = new DecompilerContext(source.Method); ast = astBuilder.Build(); } else if (source.IsAst) { context = new DecompilerContext(source.Method); ast = source.Ast; } else { throw new NotSupportedException("Unknown source"); } // Optimize AST var astOptimizer = new AstOptimizer(context, ast); astOptimizer.Optimize(); // Optimize AST towards the target TargetConverters.Convert(context, ast, source, compiler); // Return return return ast; }
/// <summary> /// Reduces the branch codes to just br and brtrue. /// Moves ILRanges to the branch argument /// </summary> void ReduceBranchInstructionSet(AstBlock block) { for (int i = 0; i < block.Body.Count; i++) { AstExpression expr = block.Body[i] as AstExpression; if (expr != null && expr.Prefixes == null) { AstCode op; switch (expr.Code) { case AstCode.Switch: case AstCode.LookupSwitch: case AstCode.Brtrue: case AstCode.Brfalse: expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges); expr.ILRanges.Clear(); continue; case AstCode.__Beq: op = AstCode.Ceq; break; case AstCode.__Bne_Un: op = AstCode.Cne; break; case AstCode.__Bgt: op = AstCode.Cgt; break; case AstCode.__Bgt_Un: op = AstCode.Cgt_Un; break; case AstCode.__Ble: op = AstCode.Cle; break; case AstCode.__Ble_Un: op = AstCode.Cle_Un; break; case AstCode.__Blt: op = AstCode.Clt; break; case AstCode.__Blt_Un: op = AstCode.Clt_Un; break; case AstCode.__Bge: op = AstCode.Cge; break; case AstCode.__Bge_Un: op = AstCode.Cge_Un; break; default: continue; } var newExpr = new AstExpression(expr.SourceLocation, op, null, expr.Arguments); block.Body[i] = new AstExpression(expr.SourceLocation, AstCode.Brtrue, expr.Operand, newExpr); newExpr.ILRanges.AddRange(expr.ILRanges); } } }
public static bool RunOptimization(this AstBlock block, Func <List <AstNode>, AstExpression, int, bool> optimization) { bool modified = false; foreach (AstBasicBlock bb in block.Body) { for (int i = bb.Body.Count - 1; i >= 0; i--) { AstExpression expr = bb.Body.ElementAtOrDefault(i) as AstExpression; if (expr != null && optimization(bb.Body, expr, i)) { modified = true; } } } return(modified); }
public SimpleControlFlow(DecompilerContext context, AstBlock method) { this.context = context; this.typeSystem = context.CurrentModule.TypeSystem; foreach (AstLabel target in method.GetSelfAndChildrenRecursive <AstExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; } foreach (AstBasicBlock bb in method.GetSelfAndChildrenRecursive <AstBasicBlock>()) { foreach (AstLabel label in bb.GetChildren().OfType <AstLabel>()) { labelToBasicBlock[label] = bb; } } }
internal static MSA.Expression /*!*/ MakeCallWithBlockRetryable(AstGenerator /*!*/ gen, MSA.Expression /*!*/ invoke, MSA.Expression blockArgVariable, MSA.Expression transformedBlock, bool isBlockDefinition) { Assert.NotNull(invoke); Debug.Assert((blockArgVariable == null) == (transformedBlock == null)); // see Ruby Language.doc/Control Flow Implementation/Method Call With a Block MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#method-result", typeof(object)); MSA.ParameterExpression evalUnwinder; MSA.LabelTarget retryLabel = Ast.Label("retry"); var result = new AstBlock { Ast.Assign(blockArgVariable, Ast.Convert(transformedBlock, blockArgVariable.Type)), Ast.Label(retryLabel), (isBlockDefinition) ? Methods.InitializeBlock.OpCall(blockArgVariable) : null, AstUtils.Try( Ast.Assign(resultVariable, invoke) ).Catch(evalUnwinder = Ast.Parameter(typeof(EvalUnwinder), "#u"), Ast.Assign( resultVariable, Ast.Field(evalUnwinder, EvalUnwinder.ReturnValueField) ) ), Ast.IfThen(Ast.TypeEqual(resultVariable, typeof(BlockReturnResult)), Ast.IfThenElse(Methods.IsRetrySingleton.OpCall(resultVariable), // retry: AstUtils.IfThenElse(Ast.Equal(gen.MakeMethodBlockParameterRead(), blockArgVariable), RetryStatement.TransformRetry(gen), Ast.Goto(retryLabel) ), // return: gen.Return(ReturnStatement.Propagate(gen, resultVariable)) ) ), resultVariable }; return(result); }
/// <summary> /// Perform all dot42 related Ast conversions. /// </summary> public static void Convert(DecompilerContext context, AstBlock ast, MethodSource currentMethod, AssemblyCompiler compiler) { if (ast.IsOptimizedForTarget) { return; } #if DEBUG //Debugger.Launch(); if ((currentMethod.Method != null) && (currentMethod.Method.Name.Equals("runTest", StringComparison.OrdinalIgnoreCase))) { //Debugger.Launch(); } #endif IntPtrConverter.Convert(ast, compiler); TypeOfConverter.Convert(ast, compiler); BranchOptimizer.Convert(ast); CompoundAssignmentConverter.Convert(ast); ByReferenceParamConverter.Convert(context, ast, compiler); CompareUnorderedConverter.Convert(ast); EnumConverter.Convert(ast, compiler); EnumOptimizer.Convert(ast, compiler); // Keep this order NullableConverter.Convert(ast, compiler); PrimitiveAddressOfConverter.Convert(ast, currentMethod, compiler); StructCallConverter.Convert(ast, compiler); // end InitializeStructVariablesConverter.Convert(ast); DelegateConverter.Convert(ast); LdcWideConverter.Convert(ast); LdLocWithConversionConverter.Convert(ast); ConvertAfterLoadConversionConverter.Convert(ast); ConvertBeforeStoreConversionConverter.Convert(ast); CleanupConverter.Convert(ast); GenericsConverter.Convert(ast); // Expand cast expressions CastConverter.Convert(ast, currentMethod, compiler); // Expand generic instance information GenericInstanceConverter.Convert(ast, currentMethod, compiler); }
/// <summary> /// Perform all dot42 related Ast conversions. /// </summary> public static void Convert(DecompilerContext context, AstBlock ast, MethodSource currentMethod, AssemblyCompiler compiler) { if (ast.IsOptimizedForTarget) return; #if DEBUG //Debugger.Launch(); if ((currentMethod.Method != null) && (currentMethod.Method.Name.Equals("runTest", StringComparison.OrdinalIgnoreCase))) { //Debugger.Launch(); } #endif IntPtrConverter.Convert(ast, compiler); TypeOfConverter.Convert(ast, compiler); BranchOptimizer.Convert(ast); CompoundAssignmentConverter.Convert(ast); ByReferenceParamConverter.Convert(context, ast, compiler); CompareUnorderedConverter.Convert(ast); EnumConverter.Convert(ast, compiler); EnumOptimizer.Convert(ast, compiler); // Keep this order NullableConverter.Convert(ast, compiler); PrimitiveAddressOfConverter.Convert(ast, currentMethod, compiler); StructCallConverter.Convert(ast, compiler); // end InitializeStructVariablesConverter.Convert(ast); DelegateConverter.Convert(ast); LdcWideConverter.Convert(ast); LdLocWithConversionConverter.Convert(ast); ConvertAfterLoadConversionConverter.Convert(ast); ConvertBeforeStoreConversionConverter.Convert(ast); CleanupConverter.Convert(ast); GenericsConverter.Convert(ast); // Expand cast expressions CastConverter.Convert(ast, currentMethod, compiler); // Expand generic instance information GenericInstanceConverter.Convert(ast, currentMethod, compiler); }
private MSA.Expression /*!*/ TransformWrite(AstGenerator /*!*/ gen, MSA.Expression /*!*/ transformedRight, bool isSimpleRhs) { var writes = new AstBlock(); MSA.Expression rightList = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(IList)); MSA.Expression result; if (isSimpleRhs) { // 1.9 returns the RHS, not an unsplatted array, if there is just a single RHS: result = gen.CurrentScope.DefineHiddenVariable("#pr", transformedRight.Type); writes.Add(Ast.Assign(result, transformedRight)); transformedRight = AstUtils.LightDynamic(ImplicitSplatAction.Make(gen.Context), typeof(IList), result); } else { result = rightList; } writes.Add(Ast.Assign(rightList, transformedRight)); for (int i = 0; i < _unsplattedValueIndex; i++) { writes.Add(_leftValues[i].TransformWrite(gen, Methods.GetArrayItem.OpCall(rightList, AstUtils.Constant(i)))); } if (HasUnsplattedValue) { MSA.Expression explicitCount = AstUtils.Constant(_leftValues.Length - 1); // remaining RHS values: MSA.Expression array = Methods.GetArrayRange.OpCall(rightList, AstUtils.Constant(_unsplattedValueIndex), explicitCount); writes.Add(_leftValues[_unsplattedValueIndex].TransformWrite(gen, array)); for (int i = _unsplattedValueIndex + 1; i < _leftValues.Length; i++) { writes.Add(_leftValues[i].TransformWrite(gen, Methods.GetTrailingArrayItem.OpCall(rightList, AstUtils.Constant(_leftValues.Length - i), explicitCount))); } } writes.Add(result); return(writes); }
/// <summary> /// Add initialization code to the given constructor for non-initialized struct fields. /// </summary> private static void AddFieldInitializationCode(MethodSource ctor, AstBlock ast) { List<XFieldDefinition> fieldsToInitialize; var declaringType = ctor.Method.DeclaringType; var fieldsThatMayNeedInitialization = declaringType.Fields.Where(x => x.IsReachable && x.FieldType.IsEnum()); var index = 0; if (ctor.Method.IsStatic) { fieldsToInitialize = fieldsThatMayNeedInitialization.Where(x => x.IsStatic && !IsInitialized(ast, x)).ToList(); foreach (var field in fieldsToInitialize) { var defaultExpr = new AstExpression(ast.SourceLocation, AstCode.DefaultValue, field.FieldType); var initExpr = new AstExpression(ast.SourceLocation, AstCode.Stsfld, field, defaultExpr); ast.Body.Insert(index++, initExpr); } } else { // If there is a this ctor being called, we do not have to initialize here. var thisCalls = ast.GetSelfAndChildrenRecursive<AstExpression>(x => (x.Code == AstCode.Call) && ((XMethodReference) x.Operand).DeclaringType.IsSame(declaringType)); if (thisCalls.Any(x => ((XMethodReference) x.Operand).Name == ".ctor")) return; fieldsToInitialize = fieldsThatMayNeedInitialization.Where(x => !x.IsStatic && !IsInitialized(ast, x)).ToList(); if (fieldsToInitialize.Any()) { var baseCall = FindFirstBaseCtorCall(ast, declaringType); var initExpressions = new List<AstExpression>(); foreach (var field in fieldsToInitialize) { var thisExpr = new AstExpression(ast.SourceLocation, AstCode.Ldthis, null); var defaultExpr = new AstExpression(ast.SourceLocation, AstCode.DefaultValue, field.FieldType); initExpressions.Add(new AstExpression(ast.SourceLocation, AstCode.Stfld, field, thisExpr, defaultExpr)); } InsertAfter(ast, baseCall, initExpressions); } } }
/// <summary> /// Convert the given method to a list of Ast nodes. /// </summary> public AstBlock Build() { if ((codeAttr == null) || (codeAttr.Code.Length == 0)) return new AstBlock((ISourceLocation)null); var body = StackAnalysis(); var offset2ByteCode = new Dictionary<int, ByteCode>(); foreach (var bc in body) { offset2ByteCode[bc.Offset] = bc; } var blockStarts = SplitInBlocks(body, validExceptionHandlers); var list = ConvertToAst(body, new HashSet<ExceptionHandler>(validExceptionHandlers), blockStarts, 0, offset2ByteCode); var ast = new AstBlock(list.Select(x => x.SourceLocation).FirstOrDefault(), list); if (methodDef.IsSynchronized) { // Wrap in synchronization block var sl = ast.SourceLocation; var tryCatch = new AstTryCatchBlock(sl); // try-lock(this) var lockExpr = methodDef.IsStatic ? new AstExpression(sl, AstCode.TypeOf, declaringType) : new AstExpression(sl, AstCode.Ldthis, null); ast.Body.Insert(0, new AstExpression(sl, AstCode.Call, MonitorMethodReference("Enter"), new AstExpression(lockExpr))); tryCatch.TryBlock = ast; // finally-unlock(this) tryCatch.FinallyBlock = new AstBlock(sl, new AstExpression(sl, AstCode.Call, MonitorMethodReference("Exit"), new AstExpression(lockExpr)), new AstExpression(sl, AstCode.Endfinally, null)); // Wrap try/catch in block ast = new AstBlock(sl, tryCatch); } return ast; }
internal MSA.Expression/*!*/ TransformStatements(MSA.Expression prologue, Statements/*!*/ statements, MSA.Expression epilogue, ResultOperation resultOperation) { Assert.NotNull(statements); int count = statements.Count + (prologue != null ? 1 : 0) + (epilogue != null ? 1 : 0); if (count == 0) { if (resultOperation.IsIgnore) { return AstUtils.Empty(); } else if (resultOperation.Variable != null) { return Ast.Assign(resultOperation.Variable, AstUtils.Constant(null, resultOperation.Variable.Type)); } else { return Ast.Return(CurrentFrame.ReturnLabel, AstUtils.Constant(null)); } } else if (count == 1) { if (prologue != null) { return prologue; } if (epilogue != null) { return epilogue; } if (resultOperation.IsIgnore) { return statements.First.Transform(this); } else { return statements.First.TransformResult(this, resultOperation); } } else { var result = new AstBlock(); if (prologue != null) { result.Add(prologue); } // transform all but the last statement if it is an expression stmt: foreach (var statement in statements.AllButLast) { result.Add(statement.Transform(this)); } if (statements.Count > 0) { if (resultOperation.IsIgnore) { result.Add(statements.Last.Transform(this)); } else { result.Add(statements.Last.TransformResult(this, resultOperation)); } } if (epilogue != null) { result.Add(epilogue); } result.Add(AstUtils.Empty()); return result; } }
internal sealed override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { string debugString = (IsSingletonDeclaration) ? "SINGLETON" : ((this is ClassDefinition) ? "CLASS" : "MODULE") + " " + QualifiedName.Name; ScopeBuilder outerLocals = gen.CurrentScope; // definition needs to take place outside the defined lexical scope: var definition = MakeDefinitionExpression(gen); var selfVariable = outerLocals.DefineHiddenVariable("#module", typeof(RubyModule)); var parentScope = gen.CurrentScopeVariable; // inner locals: ScopeBuilder scope = DefineLocals(); var scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyScope)); gen.EnterModuleDefinition( scope, selfVariable, scopeVariable, IsSingletonDeclaration ); // transform body: MSA.Expression transformedBody = Body.TransformRead(gen); // outer local: MSA.Expression resultVariable = outerLocals.DefineHiddenVariable("#result", transformedBody.Type); // begin with new scope // self = DefineModule/Class(... parent scope here ...) // <body> // end MSA.Expression result = new AstBlock { gen.DebugMarker(debugString), Ast.Assign(selfVariable, definition), scope.CreateScope( scopeVariable, Methods.CreateModuleScope.OpCall( scope.MakeLocalsStorage(), scope.GetVariableNamesExpression(), parentScope, selfVariable ), Ast.Block( Ast.Assign(resultVariable, transformedBody), AstUtils.Empty() ) ), gen.DebugMarker("END OF " + debugString), resultVariable }; gen.LeaveModuleDefinition(); return result; }
private MSA.Expression/*!*/ TransformWrite(AstGenerator/*!*/ gen, MSA.Expression/*!*/ transformedRight, bool isSimpleRhs) { var writes = new AstBlock(); MSA.Expression rightList = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(IList)); MSA.Expression result; if (isSimpleRhs) { // 1.9 returns the RHS, not an unsplatted array, if there is just a single RHS: result = gen.CurrentScope.DefineHiddenVariable("#pr", transformedRight.Type); writes.Add(Ast.Assign(result, transformedRight)); transformedRight = AstUtils.LightDynamic(ImplicitSplatAction.Make(gen.Context), typeof(IList), result); } else { result = rightList; } writes.Add(Ast.Assign(rightList, transformedRight)); for (int i = 0; i < _unsplattedValueIndex; i++) { writes.Add(_leftValues[i].TransformWrite(gen, Methods.GetArrayItem.OpCall(rightList, AstUtils.Constant(i)))); } if (HasUnsplattedValue) { MSA.Expression explicitCount = AstUtils.Constant(_leftValues.Length - 1); // remaining RHS values: MSA.Expression array = Methods.GetArrayRange.OpCall(rightList, AstUtils.Constant(_unsplattedValueIndex), explicitCount); writes.Add(_leftValues[_unsplattedValueIndex].TransformWrite(gen, array)); for (int i = _unsplattedValueIndex + 1; i < _leftValues.Length; i++) { writes.Add(_leftValues[i].TransformWrite(gen, Methods.GetTrailingArrayItem.OpCall(rightList, AstUtils.Constant(_leftValues.Length - i), explicitCount))); } } writes.Add(result); return writes; }
internal static MSA.Expression/*!*/ MakeCallWithBlockRetryable(AstGenerator/*!*/ gen, MSA.Expression/*!*/ invoke, MSA.Expression blockArgVariable, MSA.Expression transformedBlock, bool isBlockDefinition) { Assert.NotNull(invoke); Debug.Assert((blockArgVariable == null) == (transformedBlock == null)); // see Ruby Language.doc/Control Flow Implementation/Method Call With a Block MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#method-result", typeof(object)); MSA.ParameterExpression evalUnwinder; MSA.LabelTarget retryLabel = Ast.Label("retry"); var result = new AstBlock { Ast.Assign(blockArgVariable, Ast.Convert(transformedBlock, blockArgVariable.Type)), Ast.Label(retryLabel), (isBlockDefinition) ? Methods.InitializeBlock.OpCall(blockArgVariable) : null, AstUtils.Try( Ast.Assign(resultVariable, invoke) ).Catch(evalUnwinder = Ast.Parameter(typeof(EvalUnwinder), "#u"), Ast.Assign( resultVariable, Ast.Field(evalUnwinder, EvalUnwinder.ReturnValueField) ) ), Ast.IfThen(Ast.TypeEqual(resultVariable, typeof(BlockReturnResult)), Ast.IfThenElse(Methods.IsRetrySingleton.OpCall(resultVariable), // retry: AstUtils.IfThenElse(Ast.Equal(gen.MakeMethodBlockParameterRead(), blockArgVariable), RetryStatement.TransformRetry(gen), Ast.Goto(retryLabel) ), // return: gen.Return(ReturnStatement.Propagate(gen, resultVariable)) ) ), resultVariable }; return result; }
// see Ruby Language.doc/Runtime/Control Flow Implementation/While-Until internal override MSA.Expression/*!*/ TransformRead(AstGenerator/*!*/ gen) { MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#loop-result", typeof(object)); MSA.Expression redoVariable = gen.CurrentScope.DefineHiddenVariable("#skip-condition", typeof(bool)); MSA.ParameterExpression unwinder; bool isInnerLoop = gen.CurrentLoop != null; MSA.LabelTarget breakLabel = Ast.Label(); MSA.LabelTarget continueLabel = Ast.Label(); gen.EnterLoop(redoVariable, resultVariable, breakLabel, continueLabel); MSA.Expression transformedBody = gen.TransformStatements(_statements, ResultOperation.Ignore); MSA.Expression transformedCondition = _condition.TransformCondition(gen, true); gen.LeaveLoop(); MSA.Expression conditionPositiveStmt, conditionNegativeStmt; if (_isWhileLoop) { conditionPositiveStmt = AstUtils.Empty(); conditionNegativeStmt = Ast.Break(breakLabel); } else { conditionPositiveStmt = Ast.Break(breakLabel); conditionNegativeStmt = AstUtils.Empty(); } // make the loop first: MSA.Expression loop = new AstBlock { gen.ClearDebugInfo(), Ast.Assign(redoVariable, AstUtils.Constant(_isPostTest)), AstFactory.Infinite(breakLabel, continueLabel, AstUtils.Try( AstUtils.If(redoVariable, Ast.Assign(redoVariable, AstUtils.Constant(false)) ).ElseIf(transformedCondition, conditionPositiveStmt ).Else( conditionNegativeStmt ), transformedBody, AstUtils.Empty() ).Catch(unwinder = Ast.Parameter(typeof(BlockUnwinder), "#u"), // redo = u.IsRedo Ast.Assign(redoVariable, Ast.Field(unwinder, BlockUnwinder.IsRedoField)), AstUtils.Empty() ).Filter(unwinder = Ast.Parameter(typeof(EvalUnwinder), "#u"), Ast.Equal(Ast.Field(unwinder, EvalUnwinder.ReasonField), AstFactory.BlockReturnReasonBreak), // result = unwinder.ReturnValue Ast.Assign(resultVariable, Ast.Field(unwinder, EvalUnwinder.ReturnValueField)), Ast.Break(breakLabel) ) ), gen.ClearDebugInfo(), AstUtils.Empty(), }; // wrap it to try finally that updates RFC state: if (!isInnerLoop) { loop = AstUtils.Try( Methods.EnterLoop.OpCall(gen.CurrentScopeVariable), loop ).Finally( Methods.LeaveLoop.OpCall(gen.CurrentScopeVariable) ); } return Ast.Block(loop, resultVariable); }
private MSA.Expression/*!*/ TransformStatementsToExpression(Statements statements, bool toBoolean, bool positive) { if (statements == null || statements.Count == 0) { return toBoolean ? AstUtils.Constant(!positive) : AstUtils.Constant(null); } var last = toBoolean ? statements.Last.TransformCondition(this, positive) : statements.Last.TransformReadStep(this); if (statements.Count == 1) { return last; } var result = new AstBlock(); foreach (var statement in statements.AllButLast) { result.Add(statement.Transform(this)); } result.Add(last); return result; }