public MethodBodyDisassemblyFormatter(MethodDefinition methodDef, MapFileLookup mapFile) { _methodDef = methodDef; _mapFile = mapFile; TypeEntry typeEntry = null; MethodEntry methodEntry = null; if (mapFile != null) { typeEntry = mapFile.GetTypeByNewName(methodDef.Owner.Fullname); if (typeEntry != null) { methodEntry = typeEntry.FindDexMethod(methodDef.Name, methodDef.Prototype.ToSignature()); } } _dissassembly = new MethodDisassembly(methodDef, mapFile, typeEntry, methodEntry); _sourceDocument = new Lazy <string[]>(() => { if (_dissassembly.MethodEntry == null) { return(null); } var pos = _mapFile.GetSourceCodePositions(_dissassembly.MethodEntry).FirstOrDefault(); if (pos == null) { return(null); } try { return(File.ReadAllLines(pos.Document.Path)); } catch (Exception) { return(null); } }); }
public string Format(FormatOptions options) { var nl = Environment.NewLine; var sb = new StringBuilder(); var body = _methodDef.Body; var cfg = new ControlFlowGraph(body); SourceCodePosition lastSource = null; _dissassembly.Format = options; var embedSource = options.HasFlag(FormatOptions.EmbedSourceCode) || options.HasFlag(FormatOptions.EmbedSourcePositions); if (embedSource && _dissassembly.MethodEntry != null) { var pos = _mapFile.GetSourceCodePositions(_dissassembly.MethodEntry).FirstOrDefault(); if (pos != null) { sb.Append(" // ----- Source Code: "); sb.Append(pos.Document.Path); sb.Append(nl); } } foreach (var block in cfg) { if (options.HasFlag(FormatOptions.ShowControlFlow)) { sb.AppendFormat(" // ----- Entry [{0}] Exit [{1}]{2}", string.Join(", ", block.EntryBlocks.Select(x => _dissassembly.FormatAddress(x.Entry).Trim())), string.Join(", ", block.ExitBlocks.Select(x => _dissassembly.FormatAddress(x.Entry).Trim())), nl); } foreach (var i in block.Instructions) { if (embedSource) { var source = _dissassembly.FindSourceCode(i.Offset, false); if (source != null && lastSource != null && source.Document.Path != lastSource.Document.Path) { // print document name. sb.Append(" // ----- "); sb.Append(source.Document.Path); sb.Append(nl); lastSource = null; } if (source == null && lastSource != null) { sb.AppendLine(" // ----- (no source)"); } else if (source != null && (lastSource == null || !source.Position.EqualExceptOffset(lastSource.Position))) { if (options.HasFlag(FormatOptions.EmbedSourcePositions)) { sb.AppendFormat(" // ----- Position: {0} - {1}{2}", source.Position.Start, source.Position.End, nl); } if (options.HasFlag(FormatOptions.EmbedSourceCode)) { string[] lines = GetSourceCodeLines(source); if (lines != null) { sb.AppendLine(" // " + string.Join(nl + " // ", lines)); } } } lastSource = source; } sb.AppendLine(_dissassembly.FormatInstruction(i)); } } sb.AppendLine(); if (body.Exceptions.Any()) { sb.AppendLine("Exception handlers:"); foreach (var handler in body.Exceptions) { sb.AppendFormat("\t{0} - {1}{2}", _dissassembly.FormatAddress(handler.TryStart), _dissassembly.FormatAddress(handler.TryEnd), nl); foreach (var c in handler.Catches) { sb.AppendFormat("\t\t{0} => {1}{2}", c.Type, _dissassembly.FormatAddress(c.Instruction), nl); } if (handler.CatchAll != null) { sb.AppendFormat("\t\t{0} => {1}{2}", "<any>", _dissassembly.FormatAddress(handler.CatchAll), nl); } } sb.AppendLine(); } if (_mapFile != null) { var typeEntry = _mapFile.GetTypeByNewName(_methodDef.Owner.Fullname); if (typeEntry != null) { var methodEntry = typeEntry.FindDexMethod(_methodDef.Name, _methodDef.Prototype.ToSignature()); if (methodEntry != null) { _registersToVariableNames = new Dictionary <string, string>(); var validParameters = methodEntry.Parameters.Where(x => !string.IsNullOrEmpty(x.Name)).ToList(); if (validParameters.Any()) { sb.AppendLine("Parameters:"); foreach (var p in validParameters) { var registerName = _dissassembly.FormatRegister(p.Register); sb.AppendFormat("\t{0} (r{1}) -> {2}{3}", registerName, p.Register, p.Name, nl); if (!string.IsNullOrEmpty(p.Name)) { _registersToVariableNames.Add(registerName, p.Name); } } sb.AppendLine(); } var validVariables = methodEntry.Variables.Where(x => !string.IsNullOrEmpty(x.Name)).ToList(); if (validVariables.Any()) { sb.AppendLine("Variables:"); foreach (var p in validVariables) { var registerName = _dissassembly.FormatRegister(p.Register); sb.AppendFormat("\t{0} -> {1}{2}", registerName, p.Name, nl); if (!string.IsNullOrEmpty(p.Name)) { _registersToVariableNames.Add(registerName, p.Name); } } sb.AppendLine(); } sb.AppendLine("Source code positions:"); Document lastDocument = null; foreach (var row in _mapFile.GetSourceCodePositions(methodEntry)) { if (row.Document != lastDocument) { sb.AppendFormat("\t{0}{1}", row.Document.Path, nl); lastDocument = row.Document; } var pos = row.Position; sb.AppendFormat("\t{0}\t({1},{2}) - ({3},{4}){5}", MethodDisassembly.FormatOffset(pos.MethodOffset), pos.Start.Line, pos.Start.Column, pos.End.Line, pos.End.Column, nl); } } } } return(sb.ToString()); }
public CacheEntry GetFromCacheImpl(MethodDefinition targetMethod, XMethodDefinition sourceMethod, AssemblyCompiler compiler, DexTargetPackage targetPackage) { if (_initialize == null) { return(null); } _initialize.Wait(); if (!IsEnabled) { return(null); } if (sourceMethod.ScopeId == null || sourceMethod.ScopeId == "(none)") { return(null); } if (IsUnderlyingCodeModified(sourceMethod)) { return(null); } Tuple <TypeEntry, MethodEntry> entry; string typeScopeId = sourceMethod.DeclaringType.ScopeId; string methodScopeId = sourceMethod.ScopeId; MethodDefinition cachedMethod; if (_methodsByScopeId.TryGetValue(Tuple.Create(typeScopeId, methodScopeId), out entry)) { cachedMethod = _dexLookup.GetMethod(entry.Item1.DexName, entry.Item2.DexName, entry.Item2.DexSignature); } else { // try directly in the dexlookup, for jar imports cachedMethod = _dexLookup.GetMethod(typeScopeId.Replace("/", "."), targetMethod.Name, targetMethod.Prototype.ToSignature()); } if (cachedMethod == null) { return(null); } if (cachedMethod.Body == null) { // I believe there is a bug in MethodExplicitInterfaceConverter generating // stubs for interfaces if they derive from an imported interface. // Bail out for now until this is fixed. DLog.Debug(DContext.CompilerCodeGenerator, "Compiler cache: no method body found on cached version of {0}, even though one was expected.", sourceMethod); return(null); } try { if (!Equals(cachedMethod.Prototype, targetMethod.Prototype)) { throw new Exception("internal error, got the wrong method."); } var body = DexMethodBodyCloner.Clone(targetMethod, cachedMethod); FixReferences(body, compiler, targetPackage); string className = entry != null ? entry.Item1.DexName : body.Owner.Owner.Fullname; var @class = _dexLookup.GetClass(className); return(new CacheEntry(body, entry != null ? entry.Item2 : null, entry != null ? _map.GetSourceCodePositions(entry.Item2) : null, @class.SourceFile)); } catch (CompilerCacheResolveException ex) { // This happens at the moment for methods using fields in the __generated class, // as well as for references to generated methods (mostly explicit interfac stubs) // during the IL conversion phase. // This also seems to happen for Framework-nested classes, maybe because these do // not get an entry in the map file. This should be fixed. // The number of these failures in my test is 890 out of ~12000. We gracefully // handle errors by re-compiling the method body. Debug.WriteLine(string.Format("Compiler cache: error while converting cached body: {0}: {1}. Not using cached body.", sourceMethod, ex.Message)); return(null); } catch (Exception ex) { DLog.Warning(DContext.CompilerCodeGenerator, "Compiler cache: exception while converting cached body: {0}: {1}. Not using cached body.", sourceMethod, ex.Message); Trace.WriteLine(string.Format("Compiler cache: error while converting cached body: {0}: {1}. Not using cached body.", sourceMethod, ex.Message)); return(null); } }