/// <summary> /// Gets the last source location that is specified in the method. /// </summary> public ISourceLocation GetLastSourceLine() { if (ilMethod != null) { if (!ilMethod.HasBody) { return(null); } var seqPoints = ilMethod.Body.Instructions.Select(x => x.SequencePoint(ilMethod.Body)).Where(x => (x != null) && !x.IsSpecial()); return(SequencePointWrapper.Wrap(seqPoints.OrderByDescending(x => x.StartLine).FirstOrDefault())); } if (javaMethod != null) { var body = javaMethod.Body; if (body == null) { return(null); } var lastLocIns = body.Instructions.OrderByDescending(x => x.LineNumber).FirstOrDefault(); if (lastLocIns != null) { return(new SourceLocation(body, lastLocIns)); } } if (ast != null) { return(ast.GetSelfAndChildrenRecursive <AstNode>().Select(x => x.SourceLocation).Where(x => x != null).OrderByDescending(x => x.StartLine).FirstOrDefault()); } return(null); }
private readonly Stack <FinallyBlockState> tryCatchStack = new Stack <FinallyBlockState>(); // holds the current finally target. /// <summary> /// Default ctor /// </summary> internal AstCompilerVisitor(AssemblyCompiler compiler, MethodSource source, DexTargetPackage targetPackage, MethodDefinition method, MethodBody body) { this.compiler = compiler; currentMethod = source; this.targetPackage = targetPackage; currentDexMethod = method; this.body = body; frame = new AstInvocationFrame(targetPackage, method, source, body); instructions = body.Instructions; // Base class class ctor for structs if (source.IsDotNet && (source.Name == ".ctor") && (source.ILMethod.DeclaringType.IsValueType)) { var ilMethod = source.ILMethod; if (!HasBaseOrThisClassCtorCall(ilMethod)) { // Add a call to the base class ctor now var seqp = SequencePointWrapper.Wrap(ilMethod.Body.Instructions.Select(x => x.SequencePoint(ilMethod.Body)).FirstOrDefault()); var baseCtor = new MethodReference(method.Owner.SuperClass, "<init>", new Prototype(PrimitiveType.Void)); this.Add(seqp, RCode.Invoke_direct, baseCtor, frame.ThisArgument); } } // Store any genericInstance argument /*if (source.Method.NeedsGenericInstanceTypeParameter && (source.Name == ".ctor")) * { * var owner = method.Owner; * var ilMethod = source.ILMethod; * var giField = owner.GenericInstanceField; * if (giField == null) * { * throw new CompilerException(string.Format("Expected GenericInstance field in {0}", ilMethod.FullName)); * } * var seqp = SequencePointWrapper.Wrap(ilMethod.Body.Instructions.Select(x => x.SequencePoint).FirstOrDefault()); * this.Add(seqp, RCode.Iput_object, giField, frame.GenericInstanceTypeArgument, frame.ThisArgument); * }*/ }
/// <summary> /// Analyse the instructions in the method code and convert them to a ByteCode list. /// </summary> private List <ByteCode> StackAnalysis() { var instrToByteCode = new Dictionary <Instruction, ByteCode>(); // Create temporary structure for the stack analysis var body = new List <ByteCode>(methodDef.Body.Instructions.Count); List <Instruction> prefixes = null; foreach (var inst in methodDef.Body.Instructions) { if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { if (prefixes == null) { prefixes = new List <Instruction>(1); } prefixes.Add(inst); continue; } var code = (AstCode)inst.OpCode.Code; var operand = inst.Operand; AstCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body); var byteCode = new ByteCode { Offset = inst.Offset, EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize, Code = code, Operand = operand, PopCount = inst.GetPopDelta(methodDef), PushCount = inst.GetPushDelta(), SequencePoint = SequencePointWrapper.Wrap(inst.SequencePoint) }; if (prefixes != null) { instrToByteCode[prefixes[0]] = byteCode; byteCode.Offset = prefixes[0].Offset; byteCode.Prefixes = prefixes.ToArray(); prefixes = null; } else { instrToByteCode[inst] = byteCode; } body.Add(byteCode); } for (int i = 0; i < body.Count - 1; i++) { body[i].Next = body[i + 1]; } var agenda = new Stack <ByteCode>(); var varCount = methodDef.Body.Variables.Count; var exceptionHandlerStarts = new HashSet <ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart])); // Add known states if (methodDef.Body.HasExceptionHandlers) { foreach (var ex in methodDef.Body.ExceptionHandlers) { var handlerStart = instrToByteCode[ex.HandlerStart]; handlerStart.StackBefore = new StackSlot[0]; handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { // Catch and Filter handlers start with the exeption on the stack var ldexception = new ByteCode() { Code = AstCode.Ldexception, Operand = ex.CatchType, PopCount = 0, PushCount = 1 }; ldexceptions[ex] = ldexception; handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) }; } agenda.Push(handlerStart); if (ex.HandlerType == ExceptionHandlerType.Filter) { var filterStart = instrToByteCode[ex.FilterStart]; var ldexception = new ByteCode { Code = AstCode.Ldexception, Operand = ex.CatchType, PopCount = 0, PushCount = 1 }; // TODO: ldexceptions[ex] = ldexception; filterStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) }; filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); agenda.Push(filterStart); } } } body[0].StackBefore = new StackSlot[0]; body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount); agenda.Push(body[0]); // Process agenda while (agenda.Count > 0) { var byteCode = agenda.Pop(); // Calculate new stack var newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode); // Calculate new variable state var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); if (byteCode.IsVariableDefinition) { newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false); } // After the leave, finally block might have touched the variables if (byteCode.Code == AstCode.Leave) { newVariableState = VariableSlot.MakeUknownState(varCount); } // Find all successors var branchTargets = new List <ByteCode>(); if (!byteCode.Code.IsUnconditionalControlFlow()) { if (exceptionHandlerStarts.Contains(byteCode.Next)) { // Do not fall though down to exception handler // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it } else { branchTargets.Add(byteCode.Next); } } if (byteCode.Operand is Instruction[]) { foreach (var inst in (Instruction[])byteCode.Operand) { var target = instrToByteCode[inst]; branchTargets.Add(target); // The target of a branch must have label if (target.Label == null) { target.Label = new AstLabel(target.SequencePoint, target.Name); } } } else if (byteCode.Operand is Instruction) { var target = instrToByteCode[(Instruction)byteCode.Operand]; branchTargets.Add(target); // The target of a branch must have label if (target.Label == null) { target.Label = new AstLabel(target.SequencePoint, target.Name); } } // Apply the state to successors foreach (var branchTarget in branchTargets) { if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) { if (branchTargets.Count == 1) { branchTarget.StackBefore = newStack; branchTarget.VariablesBefore = newVariableState; } else { // Do not share data for several bytecodes branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null); branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); } agenda.Push(branchTarget); } else { if (branchTarget.StackBefore.Length != newStack.Length) { throw new Exception("Inconsistent stack size at " + byteCode.Name); } // Be careful not to change our new data - it might be reused for several branch targets. // In general, be careful that two bytecodes never share data structures. bool modified = false; // Merge stacks - modify the target for (int i = 0; i < newStack.Length; i++) { var oldDefs = branchTarget.StackBefore[i].Definitions; var newDefs = oldDefs.Union(newStack[i].Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.StackBefore[i] = new StackSlot(newDefs, null); modified = true; } } // Merge variables - modify the target for (int i = 0; i < newVariableState.Length; i++) { var oldSlot = branchTarget.VariablesBefore[i]; var newSlot = newVariableState[i]; if (!oldSlot.UnknownDefinition) { if (newSlot.UnknownDefinition) { branchTarget.VariablesBefore[i] = newSlot; modified = true; } else { ByteCode[] oldDefs = oldSlot.Definitions; ByteCode[] newDefs = CollectionExtensions.Union(oldDefs, newSlot.Definitions); if (newDefs.Length > oldDefs.Length) { branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false); modified = true; } } } } if (modified) { agenda.Push(branchTarget); } } } } // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid) // I belive it is safe to just remove it body.RemoveAll(b => b.StackBefore == null); // Genertate temporary variables to replace stack foreach (var byteCode in body) { int argIdx = 0; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null); byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar); foreach (ByteCode pushedBy in byteCode.StackBefore[i].Definitions) { if (pushedBy.StoreTo == null) { pushedBy.StoreTo = new List <AstVariable>(1); } pushedBy.StoreTo.Add(tmpVar); } argIdx++; } } // Try to use single temporary variable insted of several if possilbe (especially useful for dup) // This has to be done after all temporary variables are assigned so we know about all loads foreach (var byteCode in body) { if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { var locVars = byteCode.StoreTo; // For each of the variables, find the location where it is loaded - there should be preciesly one var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); // We now know that all the variables have a single load, // Let's make sure that they have also a single store - us if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode)) { // Great - we can reduce everything into single variable var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault()); byteCode.StoreTo = new List <AstVariable>() { tmpVar }; foreach (var bc in body) { for (int i = 0; i < bc.StackBefore.Length; i++) { // Is it one of the variable to be merged? if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { // Replace with the new temp variable bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar); } } } } } } // Split and convert the normal local variables ConvertLocalVariables(body); // Convert branch targets to labels and references to xreferences foreach (var byteCode in body) { if (byteCode.Operand is Instruction[]) { byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label).ToArray(); } else if (byteCode.Operand is Instruction) { byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label; } else if (byteCode.Operand is FieldReference) { byteCode.Operand = XBuilder.AsFieldReference(module, (FieldReference)byteCode.Operand); } else if (byteCode.Operand is MethodReference) { byteCode.Operand = XBuilder.AsMethodReference(module, (MethodReference)byteCode.Operand); } else if (byteCode.Operand is TypeReference) { byteCode.Operand = XBuilder.AsTypeReference(module, (TypeReference)byteCode.Operand); } } // Convert parameters to ILVariables ConvertParameters(body); return(body); }