diff --git a/src/Neo.Compiler.CSharp/CompilationContext.cs b/src/Neo.Compiler.CSharp/CompilationContext.cs index 55830d5c2..d8ff7a126 100644 --- a/src/Neo.Compiler.CSharp/CompilationContext.cs +++ b/src/Neo.Compiler.CSharp/CompilationContext.cs @@ -1,10 +1,10 @@ // Copyright (C) 2015-2022 The Neo Project. -// -// The Neo.Compiler.CSharp is free software distributed under the MIT -// software license, see the accompanying file LICENSE in the main directory -// of the project or http://www.opensource.org/licenses/mit-license.php +// +// The Neo.Compiler.CSharp is free software distributed under the MIT +// software license, see the accompanying file LICENSE in the main directory +// of the project or http://www.opensource.org/licenses/mit-license.php // for more details. -// +// // Redistribution and use in source and binary forms with or without // modifications are permitted. @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; using Neo.Cryptography.ECC; using Neo.Json; using Neo.SmartContract; @@ -327,15 +328,36 @@ public JObject CreateManifest() }; } - public JObject CreateDebugInformation() + public JObject CreateDebugInformation(string folder = "") { - string[] sourceLocations = GetSourceLocations(compilation).Distinct().ToArray(); - return new JObject + List documents = new(); + List methods = new(); + foreach (var m in methodsConverted.Where(p => p.SyntaxNode is not null)) { - ["hash"] = Script.ToScriptHash().ToString(), - ["documents"] = sourceLocations.Select(p => (JString)p!).ToArray(), - ["static-variables"] = staticFields.OrderBy(p => p.Value).Select(p => ((JString)$"{p.Key.Name},{p.Key.Type.GetContractParameterType()},{p.Value}")!).ToArray(), - ["methods"] = methodsConverted.Where(p => p.SyntaxNode is not null).Select(m => new JObject + List sequencePoints = new(); + foreach (var ins in m.Instructions.Where(i => i.SourceLocation is not null)) + { + var doc = ins.SourceLocation!.SourceTree!.FilePath; + if (!string.IsNullOrEmpty(folder)) + { + doc = Path.GetRelativePath(folder, doc); + } + + var index = documents.IndexOf(doc); + if (index == -1) + { + index = documents.Count; + documents.Add(doc); + } + + FileLinePositionSpan span = ins.SourceLocation!.GetLineSpan(); + var str = $"{ins.Offset}[{index}]{ToRangeString(span.StartLinePosition)}-{ToRangeString(span.EndLinePosition)}"; + sequencePoints.Add(new JString(str)); + + static string ToRangeString(LinePosition pos) => $"{pos.Line + 1}:{pos.Character + 1}"; + } + + methods.Add(new JObject { ["id"] = m.Symbol.ToString(), ["name"] = $"{m.Symbol.ContainingType},{m.Symbol.Name}", @@ -346,12 +368,17 @@ public JObject CreateDebugInformation() .ToArray(), ["return"] = m.Symbol.ReturnType.GetContractParameterType().ToString(), ["variables"] = m.Variables.Select(p => ((JString)$"{p.Symbol.Name},{p.Symbol.Type.GetContractParameterType()},{p.SlotIndex}")!).ToArray(), - ["sequence-points"] = m.Instructions.Where(p => p.SourceLocation is not null).Select(p => - { - FileLinePositionSpan span = p.SourceLocation!.GetLineSpan(); - return ((JString)$"{p.Offset}[{Array.IndexOf(sourceLocations, p.SourceLocation.SourceTree!.FilePath)}]{span.StartLinePosition.Line + 1}:{span.StartLinePosition.Character + 1}-{span.EndLinePosition.Line + 1}:{span.EndLinePosition.Character + 1}")!; - }).ToArray() - }).ToArray(), + ["sequence-points"] = sequencePoints.ToArray(), + }); + } + + return new JObject + { + ["hash"] = Script.ToScriptHash().ToString(), + ["documents"] = documents.Select(p => (JString)p!).ToArray(), + ["document-root"] = string.IsNullOrEmpty(folder) ? JToken.Null : folder, + ["static-variables"] = staticFields.OrderBy(p => p.Value).Select(p => ((JString)$"{p.Key.Name},{p.Key.Type.GetContractParameterType()},{p.Value}")!).ToArray(), + ["methods"] = methods.ToArray(), ["events"] = eventsExported.Select(e => new JObject { ["id"] = e.Name, @@ -361,15 +388,6 @@ public JObject CreateDebugInformation() }; } - private static IEnumerable GetSourceLocations(Compilation compilation) - { - foreach (SyntaxTree syntaxTree in compilation.SyntaxTrees) - yield return syntaxTree.FilePath; - foreach (CompilationReference reference in compilation.References.OfType()) - foreach (string path in GetSourceLocations(reference.Compilation)) - yield return path; - } - private void ProcessCompilationUnit(HashSet processed, SemanticModel model, CompilationUnitSyntax syntax) { foreach (MemberDeclarationSyntax member in syntax.Members) diff --git a/src/Neo.Compiler.CSharp/Program.cs b/src/Neo.Compiler.CSharp/Program.cs index 2962d6614..47b6264f0 100644 --- a/src/Neo.Compiler.CSharp/Program.cs +++ b/src/Neo.Compiler.CSharp/Program.cs @@ -1,10 +1,10 @@ // Copyright (C) 2015-2022 The Neo Project. -// -// The Neo.Compiler.CSharp is free software distributed under the MIT -// software license, see the accompanying file LICENSE in the main directory -// of the project or http://www.opensource.org/licenses/mit-license.php +// +// The Neo.Compiler.CSharp is free software distributed under the MIT +// software license, see the accompanying file LICENSE in the main directory +// of the project or http://www.opensource.org/licenses/mit-license.php // for more details. -// +// // Redistribution and use in source and binary forms with or without // modifications are permitted. @@ -127,26 +127,26 @@ private static int ProcessOutputs(Options options, string folder, CompilationCon if (context.Success) { string baseName = options.BaseName ?? context.ContractName!; - folder = options.Output ?? Path.Combine(folder, "bin", "sc"); - Directory.CreateDirectory(folder); - string path = Path.Combine(folder, $"{baseName}.nef"); + string outputFolder = options.Output ?? Path.Combine(folder, "bin", "sc"); + Directory.CreateDirectory(outputFolder); + string path = Path.Combine(outputFolder, $"{baseName}.nef"); File.WriteAllBytes(path, context.CreateExecutable().ToArray()); Console.WriteLine($"Created {path}"); - path = Path.Combine(folder, $"{baseName}.manifest.json"); + path = Path.Combine(outputFolder, $"{baseName}.manifest.json"); File.WriteAllBytes(path, context.CreateManifest().ToByteArray(false)); Console.WriteLine($"Created {path}"); if (options.Debug) { - path = Path.Combine(folder, $"{baseName}.nefdbgnfo"); + path = Path.Combine(outputFolder, $"{baseName}.nefdbgnfo"); using FileStream fs = new(path, FileMode.Create, FileAccess.Write); using ZipArchive archive = new(fs, ZipArchiveMode.Create); using Stream stream = archive.CreateEntry($"{baseName}.debug.json").Open(); - stream.Write(context.CreateDebugInformation().ToByteArray(false)); + stream.Write(context.CreateDebugInformation(folder).ToByteArray(false)); Console.WriteLine($"Created {path}"); } if (options.Assembly) { - path = Path.Combine(folder, $"{baseName}.asm"); + path = Path.Combine(outputFolder, $"{baseName}.asm"); File.WriteAllText(path, context.CreateAssembly()); Console.WriteLine($"Created {path}"); }