Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
Relfos committed Mar 16, 2022
2 parents c8e86c5 + fa55084 commit f5c91b1
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 12 deletions.
8 changes: 6 additions & 2 deletions Compiler/AST/MethodInterface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public class MethodInterface
public int StartAsmLine;
public int EndAsmLine;

public MethodInterface(LibraryDeclaration library, MethodImplementationType implementation, string name, bool isPublic, MethodKind kind, VarType returnType, MethodParameter[] parameters, string alias = null)
public bool IsMulti; // if true, method will emit multiple return values in the vm result stack

public MethodInterface(LibraryDeclaration library, MethodImplementationType implementation, string name, bool isPublic, MethodKind kind, VarType returnType, MethodParameter[] parameters, string alias = null, bool isMulti = false)
{
this.Name = name;
this.Library = library;
Expand All @@ -79,9 +81,11 @@ public MethodInterface(LibraryDeclaration library, MethodImplementationType impl
this.IsPublic = isPublic;
this.ReturnType = returnType;
this.Parameters = parameters;
this.IsMulti = isMulti;

this.PreCallback = null;
this.PostCallback = null;

this.Contract = this.Library.Name;

if (alias != null)
Expand Down
8 changes: 7 additions & 1 deletion Compiler/AST/Statements/ReturnStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public override void GenerateCode(CodeGenerator output)
var returnType = this.method.@interface.ReturnType;

var simpleReturn = (this.method.ParentScope.Module is Script);
var isMulti = this.method.@interface.IsMulti;

if (expression != null)
{
Expand All @@ -48,11 +49,16 @@ public override void GenerateCode(CodeGenerator output)
Compiler.Instance.DeallocRegister(ref reg);
}
else
if (returnType.Kind != VarKind.None)
if (returnType.Kind != VarKind.None && !isMulti)
{
throw new System.Exception($"expected return expression for non-void method: {method.Name}");
}

if (isMulti && expression != null)
{
return; // for multi methods a return with expression is basically just a push, nothing more..
}

if (simpleReturn)
{
output.AppendLine(this, "RET");
Expand Down
9 changes: 7 additions & 2 deletions Compiler/CodeGen/Contract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public override ContractInterface GenerateCode(CodeGenerator output)
return abi;
}

public MethodDeclaration AddMethod(int line, string name, bool isPublic, MethodKind kind, VarType returnType, MethodParameter[] parameters, Scope scope)
public MethodDeclaration AddMethod(int line, string name, bool isPublic, MethodKind kind, VarType returnType, MethodParameter[] parameters, Scope scope, bool isMulti)
{
if (Methods.Count == 0)
{
Expand All @@ -203,7 +203,12 @@ public MethodDeclaration AddMethod(int line, string name, bool isPublic, MethodK
throw new CompilerException($"Invalid method definition: {name}:{returnType}");
}

var method = new MethodInterface(this.library, MethodImplementationType.Custom, name, isPublic, kind, returnType, parameters);
if (Methods.ContainsKey(name))
{
throw new CompilerException($"Duplicated method name: {name}:{returnType}");
}

var method = new MethodInterface(this.library, MethodImplementationType.Custom, name, isPublic, kind, returnType, parameters, null, isMulti);
this.Scope.Methods.Add(method);

var decl = new MethodDeclaration(scope, method);
Expand Down
33 changes: 27 additions & 6 deletions Compiler/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,12 @@ private void ParseModule(Module module)
var value = (byte)((byte)EventKind.Custom + contract.Events.Count);

var eventDecl = new EventDeclaration(module.Scope, eventName, value, eventType, description);

if (contract.Events.ContainsKey(eventName))
{
throw new CompilerException($"duplicated event: {eventName}");
}

contract.Events[eventName] = eventDecl;
}
else
Expand All @@ -661,7 +667,7 @@ private void ParseModule(Module module)
throw new CompilerException("constructor must have only one parameter of type address");
}

var method = contract.AddMethod(line, name, true, MethodKind.Constructor, VarType.Find(VarKind.None), parameters, scope);
var method = contract.AddMethod(line, name, true, MethodKind.Constructor, VarType.Find(VarKind.None), parameters, scope, false);

ExpectToken("{");

Expand Down Expand Up @@ -696,7 +702,7 @@ private void ParseModule(Module module)
propertyName = "get" + char.ToUpper(propertyName[0]) + propertyName.Substring(1);
}

var method = contract.AddMethod(line, propertyName, true, MethodKind.Property, returnType, parameters, scope);
var method = contract.AddMethod(line, propertyName, true, MethodKind.Property, returnType, parameters, scope, false);

var next = FetchToken();
if (next.value == "=")
Expand Down Expand Up @@ -748,17 +754,29 @@ private void ParseModule(Module module)

var returnType = VarType.Find(VarKind.None);

var isMulti = false;

var next = FetchToken();
if (next.value == ":")
{
returnType = ExpectType();

next = FetchToken();
if (next.value == "*")
{
isMulti = true;
}
else
{
Rewind();
}
}
else
{
Rewind();
}

var method = contract.AddMethod(line, name, token.value == "public", MethodKind.Method, returnType, parameters, scope);
var method = contract.AddMethod(line, name, token.value == "public", MethodKind.Method, returnType, parameters, scope, isMulti);

ExpectToken("{");
contract.SetMethodBody(name, ParseCommandBlock(scope, method));
Expand Down Expand Up @@ -791,7 +809,7 @@ private void ParseModule(Module module)

var scope = new Scope(module.Scope, name, parameters);

var method = contract.AddMethod(line, name, true, MethodKind.Task, VarType.Find(VarKind.Bool), parameters, scope);
var method = contract.AddMethod(line, name, true, MethodKind.Task, VarType.Find(VarKind.Bool), parameters, scope, false);

ExpectToken("{");
contract.SetMethodBody(name, ParseCommandBlock(scope, method));
Expand Down Expand Up @@ -887,7 +905,7 @@ private void ParseModule(Module module)

var scope = new Scope(module.Scope, name, parameters);

var method = contract.AddMethod(line, name, true, MethodKind.Trigger, VarType.Find(VarKind.None), parameters, scope);
var method = contract.AddMethod(line, name, true, MethodKind.Trigger, VarType.Find(VarKind.None), parameters, scope, false);

ExpectToken("{");
contract.SetMethodBody(name, ParseCommandBlock(scope, method));
Expand Down Expand Up @@ -1130,7 +1148,10 @@ private StatementBlock ParseCommandBlock(Scope scope, MethodDeclaration method)
block.Commands.Add(new ReturnStatement(method, expr));
ExpectToken(";");

terminateEarly = true;
if (!method.@interface.IsMulti || expr == null)
{
terminateEarly = true;
}
break;
}

Expand Down
1 change: 0 additions & 1 deletion Compiler/builtins.tomb
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,4 @@ contract builtins {

return -1; // not found
}

}
80 changes: 80 additions & 0 deletions Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,28 @@ public check(x:number): string {
}


[Test]
public void DuplicatedMethodNames()
{
var sourceCode =
@"
contract test {
public testme(x:number): number {
return 5;
}
public testme(x:number): string {
return ""zero"";
}}";

var parser = new Compiler(DomainSettings.LatestKnownProtocol);

Assert.Catch<CompilerException>(() =>
{
var contract = parser.Process(sourceCode).First();
});
}

[Test]
public void TestCounter()
{
Expand Down Expand Up @@ -2351,5 +2373,63 @@ public exist(nftID:number): bool {
var exists = callResult.AsBool();
Assert.IsFalse(exists, "It shouldn't exist...");
}

[Test]
public void MultiResultsSimple()
{
var sourceCode =
@"
contract test{
public getStrings(): string* {
return ""hello"";
return ""world"";
}
}";

var parser = new Compiler(DomainSettings.LatestKnownProtocol);
var contract = parser.Process(sourceCode).First();

var storage = new Dictionary<byte[], byte[]>(new ByteArrayComparer());

TestVM vm;

var getStrings = contract.abi.FindMethod("getStrings");
Assert.IsNotNull(getStrings);

vm = new TestVM(contract, storage, getStrings);
var result = vm.Execute();
Assert.IsTrue(result == ExecutionState.Halt);

Assert.IsTrue(vm.Stack.Count == 2);

var obj = vm.Stack.Pop();
var x = obj.AsString();
Assert.IsTrue(x == "world");

obj = vm.Stack.Pop();
x = obj.AsString();
Assert.IsTrue(x == "hello");
}

[Test]
public void MultiResultsEarlyReturn()
{
var sourceCode =
@"
contract test{
public getStrings(): string* {
return ""ok"";
return;
return ""bug""; // this line should not compile
}
}";

var parser = new Compiler(DomainSettings.LatestKnownProtocol);

Assert.Catch<CompilerException>(() => {
var contract = parser.Process(sourceCode).First();
});
}

}
}

0 comments on commit f5c91b1

Please sign in to comment.