Skip to content

Commit

Permalink
Merge branch 'master' into add-examples
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/Neo.Compiler.CSharp/CompilationContext.cs
#	src/Neo.SmartContract.Template/templates/neocontract/Contract1.cs
  • Loading branch information
Jim8y committed Feb 25, 2024
2 parents 40ea594 + 7add5a9 commit 1b9d962
Show file tree
Hide file tree
Showing 105 changed files with 4,730 additions and 2,618 deletions.
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"name": "C# (.NET)",
"image": "mcr.microsoft.com/devcontainers/dotnet:1-7.0-jammy",
"onCreateCommand": "./scripts/load_submodule.sh",
"postCreateCommand": "dotnet restore && dotnet build",
"customizations": {
"vscode": {
"extensions": [
Expand Down
26 changes: 14 additions & 12 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Check format
run: |
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
Expand All @@ -32,44 +35,43 @@ jobs:
dotnet new uninstall Neo.SmartContract.Template
dotnet remove ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj package Neo.SmartContract.Framework
dotnet add ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj reference ./src/Neo.SmartContract.Framework/Neo.SmartContract.Framework.csproj
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll -d ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Artifacts/ --generate-artifacts source
dotnet ./src/Neo.Compiler.CSharp/bin/Debug/net7.0/nccs.dll ./src/Neo.SmartContract.Template/bin/Debug/Nep17Contract.csproj -o ./tests/Neo.SmartContract.Template.UnitTests/templates/neocontractnep17/Artifacts/ --generate-artifacts source --debug
- name: Build Solution
run: dotnet build ./neo-devpack-dotnet.sln
- name: Check format
run: |
dotnet format --no-restore --verify-no-changes --verbosity diagnostic
- name: Add package coverlet.msbuild
run: find tests -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild
- name: Test Neo.Compiler.CSharp.UnitTests
run: |
dotnet test ./tests/Neo.Compiler.CSharp.UnitTests \
--no-build \
-p:CollectCoverage=true \
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage-join/ \
-p:Exclude=\"[Neo.SmartContract.TestEngine]*,[Neo.Compiler.CSharp.UnitTests]*,[Neo]*,[Neo.IO]*,[Neo.Json]*,[Neo.VM]*,[Neo.Extensions]*,[Neo.Cryptography.BLS12_381]*\"
- name: Test Neo.SmartContract.Framework.UnitTests
run: |
dotnet test ./tests/Neo.SmartContract.Framework.UnitTests \
--no-build \
-l "console;verbosity=normal" \
-p:CollectCoverage=true \
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/lcov \
-p:MergeWith=${GITHUB_WORKSPACE}/coverage/coverage.json \
-p:Exclude=\"[Neo.Compiler.CSharp.UnitTests]*\" \
-p:CoverletOutputFormat=lcov
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage-join/ \
-p:MergeWith=${GITHUB_WORKSPACE}/coverage-join/coverage.json \
-p:Exclude=\"[Neo.SmartContract.TestEngine]*,[Neo.Compiler.CSharp.UnitTests]*,[Neo]*,[Neo.IO]*,[Neo.Json]*,[Neo.VM]*,[Neo.Extensions]*,[Neo.Cryptography.BLS12_381]*\"
- name: Test Neo.SmartContract.Template.UnitTests
run: |
dotnet test ./tests/Neo.SmartContract.Template.UnitTests \
--no-build \
-l "console;verbosity=detailed" \
-p:CollectCoverage=true \
-p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/lcov \
-p:MergeWith=${GITHUB_WORKSPACE}/coverage/coverage.json \
-p:Exclude=\"[Neo.Compiler.CSharp.UnitTests]*\" \
-p:MergeWith=${GITHUB_WORKSPACE}/coverage-join/coverage.json \
-p:Exclude=\"[Neo.SmartContract.TestEngine]*,[Neo.Compiler.CSharp.UnitTests]*,[Neo]*,[Neo.IO]*,[Neo.Json]*,[Neo.VM]*,[Neo.Extensions]*,[Neo.Cryptography.BLS12_381]*\" \
-p:CoverletOutputFormat=lcov
- name: Coveralls
uses: coverallsapp/github-action@master
uses: coverallsapp/github-action@v2.2.3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
format: lcov
file: ${GITHUB_WORKSPACE}/coverage/lcov.info

PublishPackage:
if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/')
Expand Down
2 changes: 1 addition & 1 deletion neo
Submodule neo updated 48 files
+13 −3 .github/workflows/main.yml
+34 −1,442 src/Neo.VM/ExecutionEngine.cs
+65 −0 src/Neo.VM/JumpTable/JumpTable.Bitwisee.cs
+390 −0 src/Neo.VM/JumpTable/JumpTable.Compound.cs
+396 −0 src/Neo.VM/JumpTable/JumpTable.Control.cs
+265 −0 src/Neo.VM/JumpTable/JumpTable.Numeric.cs
+213 −0 src/Neo.VM/JumpTable/JumpTable.Push.cs
+363 −0 src/Neo.VM/JumpTable/JumpTable.Slot.cs
+106 −0 src/Neo.VM/JumpTable/JumpTable.Splice.cs
+124 −0 src/Neo.VM/JumpTable/JumpTable.Stack.cs
+60 −0 src/Neo.VM/JumpTable/JumpTable.Types.cs
+71 −0 src/Neo.VM/JumpTable/JumpTable.cs
+1 −0 src/Neo.VM/Neo.VM.csproj
+2 −1 src/Neo/Hardfork.cs
+1 −0 src/Neo/Neo.csproj
+2 −1 src/Neo/NeoSystem.cs
+1 −1 src/Neo/Persistence/DataCache.cs
+1 −1 src/Neo/Persistence/MemoryStoreProvider.cs
+47 −5 src/Neo/ProtocolSettings.cs
+1 −1 src/Neo/SmartContract/ApplicationEngine.Contract.cs
+92 −50 src/Neo/SmartContract/ApplicationEngine.cs
+3 −1 src/Neo/SmartContract/IApplicationEngineProvider.cs
+165 −0 src/Neo/SmartContract/Native/ContractEventAttribute.cs
+33 −56 src/Neo/SmartContract/Native/ContractManagement.cs
+10 −0 src/Neo/SmartContract/Native/ContractMethodAttribute.cs
+4 −0 src/Neo/SmartContract/Native/ContractMethodMetadata.cs
+17 −1 src/Neo/SmartContract/Native/CryptoLib.cs
+9 −31 src/Neo/SmartContract/Native/FungibleToken.cs
+7 −3 src/Neo/SmartContract/Native/GasToken.cs
+1 −3 src/Neo/SmartContract/Native/LedgerContract.cs
+139 −32 src/Neo/SmartContract/Native/NativeContract.cs
+21 −64 src/Neo/SmartContract/Native/NeoToken.cs
+15 −56 src/Neo/SmartContract/Native/OracleContract.cs
+8 −7 src/Neo/SmartContract/Native/PolicyContract.cs
+4 −27 src/Neo/SmartContract/Native/RoleManagement.cs
+1 −1 src/Neo/SmartContract/Native/StdLib.cs
+10 −0 src/Neo/SmartContract/StorageItem.cs
+90 −0 src/Neo/Wallets/Helper.cs
+1 −88 src/Neo/Wallets/Wallet.cs
+151 −0 tests/Neo.UnitTests/SmartContract/Native/UT_ContractEventAttribute.cs
+32 −0 tests/Neo.UnitTests/SmartContract/Native/UT_ContractMethodAttribute.cs
+93 −0 tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs
+21 −0 tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs
+5 −4 tests/Neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs
+3 −3 tests/Neo.UnitTests/SmartContract/UT_InteropService.cs
+3 −3 tests/Neo.UnitTests/SmartContract/UT_JsonSerializer.cs
+127 −0 tests/Neo.UnitTests/UT_ProtocolSettings.cs
+15 −4 tests/Neo.VM.Tests/Types/TestEngine.cs
320 changes: 102 additions & 218 deletions src/Neo.Compiler.CSharp/CompilationContext.cs

Large diffs are not rendered by default.

249 changes: 249 additions & 0 deletions src/Neo.Compiler.CSharp/CompilationEngine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// Copyright (C) 2015-2024 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.

extern alias scfx;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Neo.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Akka.Util.Internal;
using BigInteger = System.Numerics.BigInteger;

namespace Neo.Compiler
{
public class CompilationEngine
{
internal Compilation? Compilation;
internal Options 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);

static CompilationEngine()
{
string coreDir = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
CommonReferences = new MetadataReference[]
{
MetadataReference.CreateFromFile(Path.Combine(coreDir, "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(coreDir, "System.Runtime.InteropServices.dll")),
MetadataReference.CreateFromFile(typeof(string).Assembly.Location),
MetadataReference.CreateFromFile(typeof(DisplayNameAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(BigInteger).Assembly.Location)
};
}

public CompilationEngine(Options options)
{
Options = options;
}

public List<CompilationContext> Compile(IEnumerable<string> sourceFiles, IEnumerable<MetadataReference> references)
{
IEnumerable<SyntaxTree> syntaxTrees = sourceFiles.OrderBy(p => p).Select(p => CSharpSyntaxTree.ParseText(File.ReadAllText(p), options: Options.GetParseOptions(), path: p));
CSharpCompilationOptions compilationOptions = new(OutputKind.DynamicallyLinkedLibrary, deterministic: true, nullableContextOptions: Options.Nullable);
Compilation = CSharpCompilation.Create(null, syntaxTrees, references, compilationOptions);
return CompileProjectContracts(Compilation);
}

public List<CompilationContext> CompileSources(string[] sourceFiles)
{
List<MetadataReference> references = new(CommonReferences)
{
MetadataReference.CreateFromFile(typeof(scfx.Neo.SmartContract.Framework.SmartContract).Assembly.Location)
};
return Compile(sourceFiles, references);
}

public List<CompilationContext> CompileProject(string csproj)
{
Compilation = GetCompilation(csproj);
return CompileProjectContracts(Compilation);
}

private List<CompilationContext> CompileProjectContracts(Compilation compilation)
{
var classDependencies = new Dictionary<INamedTypeSymbol, List<INamedTypeSymbol>>(SymbolEqualityComparer.Default);
var allSmartContracts = new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);

foreach (var tree in compilation.SyntaxTrees)
{
var semanticModel = compilation.GetSemanticModel(tree);
var classNodes = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>();

foreach (var classNode in classNodes)
{
var classSymbol = semanticModel.GetDeclaredSymbol(classNode);
if (classSymbol != null && IsDerivedFromSmartContract(classSymbol, "Neo.SmartContract.Framework.SmartContract", semanticModel))
{
allSmartContracts.Add(classSymbol);
classDependencies[classSymbol] = new List<INamedTypeSymbol>();
foreach (var member in classSymbol.GetMembers())
{
var memberTypeSymbol = (member as IFieldSymbol)?.Type ?? (member as IPropertySymbol)?.Type;
if (memberTypeSymbol is INamedTypeSymbol namedTypeSymbol && allSmartContracts.Contains(namedTypeSymbol))
{
classDependencies[classSymbol].Add(namedTypeSymbol);
}
}
}
}
}

// Verify if there is any valid smart contract class
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);
foreach (var classSymbol in sortedClasses)
{
new CompilationContext(this, classSymbol).Compile();
}

return Contexts.Select(p => p.Value).ToList();
}

private static List<INamedTypeSymbol> TopologicalSort(Dictionary<INamedTypeSymbol, List<INamedTypeSymbol>> dependencies)
{
var sorted = new List<INamedTypeSymbol>();
var visited = new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default);
var visiting = new HashSet<INamedTypeSymbol>(SymbolEqualityComparer.Default); // 添加中间状态以检测循环依赖

void Visit(INamedTypeSymbol classSymbol)
{
if (visited.Contains(classSymbol))
{
return;
}
if (!visiting.Add(classSymbol))
{
throw new InvalidOperationException("Cyclic dependency detected");
}

if (dependencies.TryGetValue(classSymbol, out var dependency))
{
foreach (var dep in dependency)
{
Visit(dep);
}
}

visiting.Remove(classSymbol);
visited.Add(classSymbol);
sorted.Add(classSymbol);
}

foreach (var classSymbol in dependencies.Keys)
{
Visit(classSymbol);
}

return sorted;
}

static bool IsDerivedFromSmartContract(INamedTypeSymbol classSymbol, string smartContractFullyQualifiedName, SemanticModel semanticModel)
{
var baseType = classSymbol.BaseType;
while (baseType != null)
{
if (baseType.ToDisplayString() == smartContractFullyQualifiedName)
{
return true;
}
baseType = baseType.BaseType;
}
return false;
}

public Compilation GetCompilation(string csproj)
{
string folder = Path.GetDirectoryName(csproj)!;
string obj = Path.Combine(folder, "obj");
HashSet<string> sourceFiles = Directory.EnumerateFiles(folder, "*.cs", SearchOption.AllDirectories)
.Where(p => !p.StartsWith(obj))
.GroupBy(Path.GetFileName)
.Select(g => g.First())
.ToHashSet(StringComparer.OrdinalIgnoreCase);
List<MetadataReference> references = new(CommonReferences);
CSharpCompilationOptions compilationOptions = new(OutputKind.DynamicallyLinkedLibrary, deterministic: true, nullableContextOptions: Options.Nullable);
XDocument document = XDocument.Load(csproj);
sourceFiles.UnionWith(document.Root!.Elements("ItemGroup").Elements("Compile").Attributes("Include").Select(p => Path.GetFullPath(p.Value, folder)));
Process.Start(new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"restore \"{csproj}\"",
WorkingDirectory = folder
})!.WaitForExit();
string assetsPath = Path.Combine(folder, "obj", "project.assets.json");
JObject assets = (JObject)JToken.Parse(File.ReadAllBytes(assetsPath))!;
foreach (var (name, package) in ((JObject)assets["targets"]![0]!).Properties)
{
MetadataReference? reference = GetReference(name, (JObject)package!, assets, folder, Options, 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)
{
string assemblyName = Path.GetDirectoryName(name)!;
if (!MetaReferences.TryGetValue(assemblyName, out var reference))
{
switch (assets["libraries"]![name]!["type"]!.GetString())
{
case "package":
string packagesPath = assets["project"]!["restore"]!["packagesPath"]!.GetString();
string namePath = assets["libraries"]![name]!["path"]!.GetString();
string[] files = ((JArray)assets["libraries"]![name]!["files"]!)
.Select(p => p!.GetString())
.Where(p => p.StartsWith("src/"))
.ToArray();
if (files.Length == 0)
{
JObject? dllFiles = (JObject?)(package["compile"] ?? package["runtime"]);
if (dllFiles is null) return null;
foreach (var (file, _) in dllFiles.Properties)
{
if (file.EndsWith("_._")) continue;
string path = Path.Combine(packagesPath, namePath, file);
if (!File.Exists(path)) continue;
reference = MetadataReference.CreateFromFile(path);
break;
}
if (reference is null) return null;
}
else
{
IEnumerable<SyntaxTree> st = files.OrderBy(p => p).Select(p => Path.Combine(packagesPath, namePath, p)).Select(p => CSharpSyntaxTree.ParseText(File.ReadAllText(p), path: p));
CSharpCompilation cr = CSharpCompilation.Create(assemblyName, st, CommonReferences, compilationOptions);
reference = cr.ToMetadataReference();
}
break;
case "project":
string msbuildProject = assets["libraries"]![name]!["msbuildProject"]!.GetString();
msbuildProject = Path.GetFullPath(msbuildProject, folder);
reference = GetCompilation(msbuildProject).ToMetadataReference();
break;
default:
throw new NotSupportedException();
}
MetaReferences.Add(assemblyName, reference);
}
return reference;
}
}
}
Loading

0 comments on commit 1b9d962

Please sign in to comment.