Skip to content

Commit

Permalink
TestEngine: Template testing optimization (#956)
Browse files Browse the repository at this point in the history
* Ensure compilation

* Update src/Neo.SmartContract.Testing/Coverage/Formats/ConsoleFormat.cs

* TODO

* Move namespace

* LF

* LF

* LF

* Fix remove

* indent comment

* Change artifact name

* Split options

* Fix Context result

* Clean debugInfo from artifacts

* Fix compilation

* Cleanup

* format

* remove artifacts

* fix nep17 template

* Fix type

* Rename artifacts

* some fixes

* Update .github/workflows/main.yml

* Clean

* Allow old code

* Fix old ECPoint

* Jimmy's feedback

* Fix

---------

Co-authored-by: Jimmy <[email protected]>
  • Loading branch information
shargon and Jim8y authored Feb 26, 2024
1 parent b6c8fb7 commit 5d4b2c0
Show file tree
Hide file tree
Showing 57 changed files with 521 additions and 283 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
dotnet format --no-restore --verify-no-changes --verbosity diagnostic
- name: Build Neo.Compiler.CSharp
run: dotnet build ./src/Neo.Compiler.CSharp/Neo.Compiler.CSharp.csproj
- name: Build Neo.SmartContract.Template and generate artifacts
- name: Build Neo.SmartContract.Template and test templates
run: |
dotnet pack ./src/Neo.SmartContract.Template/Neo.SmartContract.Template.csproj
dotnet new install ./src/Neo.SmartContract.Template/bin/Debug/Neo.SmartContract.Template.*.nupkg
Expand All @@ -41,9 +41,9 @@ jobs:
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/ownable/Ownable.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet remove ./src/Neo.SmartContract.Template/bin/Debug/oracle/OracleRequest.csproj package Neo.SmartContract.Framework
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/oracle/OracleRequest.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/nep17/Nep17Contract.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Artifacts/ --generate-artifacts source --debug
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/ownable/Ownable.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractowner/Artifacts/ --generate-artifacts source --debug
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/oracle/OracleRequest.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractoracle/Artifacts/ --generate-artifacts source --debug
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/nep17/Nep17Contract.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Artifacts/
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/ownable/Ownable.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractowner/Artifacts/
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/oracle/OracleRequest.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractoracle/Artifacts/
- name: Build Solution
run: dotnet build ./neo-devpack-dotnet.sln
- name: Add package coverlet.msbuild
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.Compiler.CSharp/CompilationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class CompilationContext
{
private readonly CompilationEngine _engine;
readonly INamedTypeSymbol _targetContract;
internal Options Options => _engine.Options;
internal CompilationOptions Options => _engine.Options;
private string? _displayName, _className;
private readonly List<Diagnostic> _diagnostics = new();
private readonly HashSet<string> _supportedStandards = new();
Expand Down
79 changes: 50 additions & 29 deletions src/Neo.Compiler.CSharp/CompilationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Neo.Compiler
public class CompilationEngine
{
internal Compilation? Compilation;
internal Options Options { get; private set; }
internal CompilationOptions Options { get; private set; }
private static readonly MetadataReference[] CommonReferences;
private static readonly Dictionary<string, MetadataReference> MetaReferences = new();
internal readonly Dictionary<INamedTypeSymbol, CompilationContext> Contexts = new(SymbolEqualityComparer.Default);
Expand All @@ -46,7 +46,7 @@ static CompilationEngine()
};
}

public CompilationEngine(Options options)
public CompilationEngine(CompilationOptions options)
{
Options = options;
}
Expand All @@ -59,11 +59,24 @@ public List<CompilationContext> Compile(IEnumerable<string> sourceFiles, IEnumer
return CompileProjectContracts(Compilation);
}

public List<CompilationContext> CompileSources(params string[] sourceFiles)
public List<CompilationContext> CompileSources(params string[] sourceFiles) => CompileSources(null, sourceFiles);

public List<CompilationContext> CompileSources((string packageName, string packageVersion) package, params string[] sourceFiles)
{
return CompileSources(new[] { package }, sourceFiles);
}

public List<CompilationContext> CompileSources(IEnumerable<(string packageName, string packageVersion)>? packages = null, params string[] sourceFiles)
{
// Generate a dummy csproj

var version = typeof(scfx.Neo.SmartContract.Framework.SmartContract).Assembly.GetName().Version!.ToString();
var packageGroup = packages is null ? "" :
$@"
<ItemGroup>
{(packages is null ? "" : string.Join(Environment.NewLine, packages.Select(u => $" <PackageReference Include =\"{u.packageName}\" Version=\"{u.packageVersion}\" />")))}
</ItemGroup>
";

var csproj = $@"
<Project Sdk=""Microsoft.NET.Sdk"">
Expand All @@ -83,9 +96,7 @@ public List<CompilationContext> CompileSources(params string[] sourceFiles)
{string.Join(Environment.NewLine, sourceFiles.Select(u => $"<Compile Include=\"{Path.GetFullPath(u)}\" />"))}
</ItemGroup>
<ItemGroup>
<PackageReference Include=""Neo.SmartContract.Framework"" Version=""{version}"" />
</ItemGroup>
{packageGroup}
</Project>";

Expand All @@ -94,7 +105,10 @@ public List<CompilationContext> CompileSources(params string[] sourceFiles)
var path = Path.GetTempFileName();
File.WriteAllText(path, csproj);

try { return CompileProject(path); }
try
{
return CompileProject(path);
}
catch { throw; }
finally { File.Delete(path); }
}
Expand Down Expand Up @@ -138,14 +152,13 @@ private List<CompilationContext> CompileProjectContracts(Compilation compilation
if (classDependencies.Count == 0) throw new FormatException("No valid neo SmartContract found. Please make sure your contract is subclass of SmartContract and is not abstract.");
// Check contract dependencies, make sure there is no cycle in the dependency graph
var sortedClasses = TopologicalSort(classDependencies);

sortedClasses.ForEach(c =>
{
var context = new CompilationContext(this, c);
context.Compile();
// Process the target contract add this compilation context
this.Contexts.Add(c, context);
});
{
var context = new CompilationContext(this, c);
context.Compile();
// Process the target contract add this compilation context
Contexts.Add(c, context);
});

return Contexts.Select(p => p.Value).ToList();
}
Expand Down Expand Up @@ -216,38 +229,46 @@ public Compilation GetCompilation(string csproj)
Process.Start(new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"restore \"{csproj}\"",
Arguments = $"restore \"{csproj}\" --source \"https://www.myget.org/F/neo/api/v3/index.json\"",
WorkingDirectory = folder
})!.WaitForExit();

// Get sources
// Parse csproj

XDocument document = XDocument.Load(csproj);
var remove = document.Root!.Elements("ItemGroup").Elements("Compile").Attributes("Remove").Select(p => p.Value).ToArray();
var obj = Path.Combine(folder, "obj");
var binSc = Path.Combine(Path.Combine(folder, "bin"), "sc");
var sourceFiles =
remove.Contains("*.cs") ? new HashSet<string>(StringComparer.OrdinalIgnoreCase) :
Directory.EnumerateFiles(folder, "*.cs", SearchOption.AllDirectories)
.Where(p => !p.StartsWith(obj) && !p.StartsWith(binSc))
.GroupBy(Path.GetFileName)
.Select(g => g.First())
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var remove = document.Root!.Elements("ItemGroup").Elements("Compile").Attributes("Remove")
.Select(p => p.Value.Contains("*") ? p.Value : Path.GetFullPath(p.Value)).ToArray();
var sourceFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

if (!remove.Contains("*.cs"))
{
var obj = Path.Combine(folder, "obj");
var binSc = Path.Combine(Path.Combine(folder, "bin"), "sc");
foreach (var entry in Directory.EnumerateFiles(folder, "*.cs", SearchOption.AllDirectories)
.Where(p => !p.StartsWith(obj) && !p.StartsWith(binSc))
.Select(u => u))
//.GroupBy(Path.GetFileName)
//.Select(g => g.First()))
{
if (!remove.Contains(entry)) sourceFiles.Add(entry);
}
}

sourceFiles.UnionWith(document.Root!.Elements("ItemGroup").Elements("Compile").Attributes("Include").Select(p => Path.GetFullPath(p.Value, folder)));
var assetsPath = Path.Combine(folder, "obj", "project.assets.json");
var assets = (JObject)JToken.Parse(File.ReadAllBytes(assetsPath))!;
List<MetadataReference> references = new(CommonReferences);
CSharpCompilationOptions compilationOptions = new(OutputKind.DynamicallyLinkedLibrary, deterministic: true, nullableContextOptions: Options.Nullable);
foreach (var (name, package) in ((JObject)assets["targets"]![0]!).Properties)
{
MetadataReference? reference = GetReference(name, (JObject)package!, assets, folder, Options, compilationOptions);
MetadataReference? reference = GetReference(name, (JObject)package!, assets, folder, compilationOptions);
if (reference is not null) references.Add(reference);
}
IEnumerable<SyntaxTree> syntaxTrees = sourceFiles.OrderBy(p => p).Select(p => CSharpSyntaxTree.ParseText(File.ReadAllText(p), options: Options.GetParseOptions(), path: p));
return CSharpCompilation.Create(assets["project"]!["restore"]!["projectName"]!.GetString(), syntaxTrees, references, compilationOptions);
}

private MetadataReference? GetReference(string name, JObject package, JObject assets, string folder, Options options, CSharpCompilationOptions compilationOptions)
private MetadataReference? GetReference(string name, JObject package, JObject assets, string folder, CSharpCompilationOptions compilationOptions)
{
string assemblyName = Path.GetDirectoryName(name)!;
if (!MetaReferences.TryGetValue(assemblyName, out var reference))
Expand Down
39 changes: 39 additions & 0 deletions src/Neo.Compiler.CSharp/CompilationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (C) 2015-2023 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
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Generic;

namespace Neo.Compiler
{
public class CompilationOptions
{
public NullableContextOptions Nullable { get; set; }
public bool Debug { get; set; }
public bool NoOptimize { get; set; }
public bool Checked { get; set; }
public bool NoInline { get; set; }
public byte AddressVersion { get; set; }
public string? BaseName { get; set; }

private CSharpParseOptions? parseOptions = null;
public CSharpParseOptions GetParseOptions()
{
if (parseOptions is null)
{
List<string> preprocessorSymbols = new();
if (Debug) preprocessorSymbols.Add("DEBUG");
parseOptions = new CSharpParseOptions(preprocessorSymbols: preprocessorSymbols);
}
return parseOptions;
}
}
}
9 changes: 6 additions & 3 deletions src/Neo.Compiler.CSharp/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,13 @@ public static ContractParameterType GetContractParameterType(this ITypeSymbol ty
case "string": return ContractParameterType.String;
case "byte[]": return ContractParameterType.ByteArray;
case "object": return ContractParameterType.Any;
case "Neo.Cryptography.ECC.ECPoint": return ContractParameterType.PublicKey;
case "Neo.Cryptography.ECC.ECPoint": // Old Neo.SmartContract.Framework
case "Neo.SmartContract.Framework.ECPoint": return ContractParameterType.PublicKey;
case "Neo.SmartContract.Framework.ByteString": return ContractParameterType.ByteArray;
case "Neo.UInt160": return ContractParameterType.Hash160;
case "Neo.UInt256": return ContractParameterType.Hash256;
case "Neo.UInt160": // Old Neo.SmartContract.Framework
case "Neo.SmartContract.Framework.UInt160": return ContractParameterType.Hash160;
case "Neo.UInt256": // Old Neo.SmartContract.Framework
case "Neo.SmartContract.Framework.UInt256": return ContractParameterType.Hash256;
case "System.Numerics.BigInteger": return ContractParameterType.Integer;
}
if (type.Name == "Map") return ContractParameterType.Map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private void ConvertObjectToString(SemanticModel model, ExpressionSyntax express
Call(NativeContract.StdLib.Hash, "itoa", 1, true);
break;
case "string":
case "Neo.Cryptography.ECC.ECPoint":
case "Neo.SmartContract.Framework.ECPoint":
case "Neo.SmartContract.Framework.ByteString":
case "Neo.SmartContract.Framework.UInt160":
case "Neo.SmartContract.Framework.UInt256":
Expand Down
27 changes: 2 additions & 25 deletions src/Neo.Compiler.CSharp/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Generic;

namespace Neo.Compiler
{
public class Options
public class Options : CompilationOptions
{
public enum GenerateArtifactsKind
{
Expand All @@ -25,26 +21,7 @@ public enum GenerateArtifactsKind
}

public string? Output { get; set; }
public string? BaseName { get; set; }
public NullableContextOptions Nullable { get; set; }
public bool Checked { get; set; }
public bool Debug { get; set; }
public bool Assembly { get; set; }
public GenerateArtifactsKind GenerateArtifacts { get; set; } = GenerateArtifactsKind.Source;
public bool NoOptimize { get; set; }
public bool NoInline { get; set; }
public byte AddressVersion { get; set; }

private CSharpParseOptions? parseOptions = null;
public CSharpParseOptions GetParseOptions()
{
if (parseOptions is null)
{
List<string> preprocessorSymbols = new();
if (Debug) preprocessorSymbols.Add("DEBUG");
parseOptions = new CSharpParseOptions(preprocessorSymbols: preprocessorSymbols);
}
return parseOptions;
}
public GenerateArtifactsKind GenerateArtifacts { get; set; } = GenerateArtifactsKind.None;
}
}
4 changes: 2 additions & 2 deletions src/Neo.Compiler.CSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private static int ProcessCsproj(Options options, string path)

private static int ProcessSources(Options options, string folder, string[] sourceFiles)
{
return ProcessOutputs(options, folder, new CompilationEngine(options).CompileSources(sourceFiles));
return ProcessOutputs(options, folder, new CompilationEngine(options).CompileSources(null, sourceFiles));
}

private static int ProcessOutputs(Options options, string folder, List<CompilationContext> contexts)
Expand Down Expand Up @@ -212,7 +212,7 @@ private static int ProcessOutput(Options options, string folder, CompilationCont

if (options.GenerateArtifacts != Options.GenerateArtifactsKind.None)
{
var artifact = manifest.GetArtifactsSource(baseName, nef, debugInfo);
var artifact = manifest.GetArtifactsSource(baseName, nef);

if (options.GenerateArtifacts == Options.GenerateArtifactsKind.All || options.GenerateArtifacts == Options.GenerateArtifactsKind.Source)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Framework/ContractParameterType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

namespace Neo.SmartContract
namespace Neo.SmartContract.Framework
{
public enum ContractParameterType : byte
{
Expand Down
3 changes: 1 addition & 2 deletions src/Neo.SmartContract.Framework/ECPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Attributes;

namespace Neo.Cryptography.ECC
namespace Neo.SmartContract.Framework
{
public abstract class ECPoint : ByteString
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Cryptography.ECC;

namespace Neo.SmartContract.Framework.Native
{
public class CalledByGroupCondition : WitnessCondition
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Framework/Native/CryptoLib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ public static partial class CryptoLib

public static extern ByteString Murmur32(ByteString value, uint seed);

public static extern bool VerifyWithECDsa(ByteString message, Cryptography.ECC.ECPoint pubkey, ByteString signature, NamedCurve curve);
public static extern bool VerifyWithECDsa(ByteString message, ECPoint pubkey, ByteString signature, NamedCurve curve);
}
}
2 changes: 0 additions & 2 deletions src/Neo.SmartContract.Framework/Native/GroupCondition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Cryptography.ECC;

namespace Neo.SmartContract.Framework.Native
{
public class GroupCondition : WitnessCondition
Expand Down
1 change: 0 additions & 1 deletion src/Neo.SmartContract.Framework/Native/NEO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

#pragma warning disable CS0626

using Neo.Cryptography.ECC;
using Neo.SmartContract.Framework.Attributes;
using Neo.SmartContract.Framework.Services;
using System.Numerics;
Expand Down
1 change: 0 additions & 1 deletion src/Neo.SmartContract.Framework/Native/NeoAccountState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Cryptography.ECC;
using System.Numerics;

namespace Neo.SmartContract.Framework.Native
Expand Down
2 changes: 1 addition & 1 deletion src/Neo.SmartContract.Framework/Native/RoleManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ public class RoleManagement
{
[ContractHash]
public static extern UInt160 Hash { get; }
public static extern Cryptography.ECC.ECPoint[] GetDesignatedByRole(Role role, uint index);
public static extern ECPoint[] GetDesignatedByRole(Role role, uint index);
}
}
2 changes: 0 additions & 2 deletions src/Neo.SmartContract.Framework/Native/Signer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Cryptography.ECC;

namespace Neo.SmartContract.Framework.Native
{
public class Signer
Expand Down
4 changes: 2 additions & 2 deletions src/Neo.SmartContract.Framework/Services/Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ public class Contract
public static extern CallFlags GetCallFlags();

[Syscall("System.Contract.CreateStandardAccount")]
public static extern UInt160 CreateStandardAccount(Cryptography.ECC.ECPoint pubKey);
public static extern UInt160 CreateStandardAccount(ECPoint pubKey);

[Syscall("System.Contract.CreateMultisigAccount")]
public static extern UInt160 CreateMultisigAccount(int m, params Cryptography.ECC.ECPoint[] pubKey);
public static extern UInt160 CreateMultisigAccount(int m, params ECPoint[] pubKey);
}
}
Loading

0 comments on commit 5d4b2c0

Please sign in to comment.