public MethodInterface AddMethod(int line, string name, bool isPublic, MethodKind kind, VarType returnType, MethodParameter[] parameters, Scope scope) { if (Methods.Count == 0) { this.LineNumber = line; } var vmType = MethodInterface.ConvertType(returnType); if (!ValidationUtils.IsValidMethod(name, vmType)) { throw new CompilerException($"Invalid method definition: {name}:{returnType}"); } var method = new MethodInterface(this.library, MethodImplementationType.Custom, name, isPublic, kind, returnType, parameters); this.Scope.Methods.Add(method); var decl = new MethodDeclaration(scope, method); decl.LineNumber = line; this.Methods[name] = decl; scope.Method = decl; return(method); }
internal ContractMethod GetABI() { var temp = new List <ContractParameter>(); foreach (var entry in [email protected]) { temp.Add(new ContractParameter(entry.Name, MethodInterface.ConvertType(entry.Type))); } return(new ContractMethod(this.Name, MethodInterface.ConvertType([email protected]), -1, temp.ToArray())); }
public override Register GenerateCode(CodeGenerator output) { var reg = expr.GenerateCode(output); switch (expr.ResultType.Kind) { case VarKind.Decimal: { switch (this.ResultType.Kind) { case VarKind.Number: return(reg); case VarKind.Decimal: { var srcDecimals = ((DecimalVarType)expr.ResultType).decimals; var dstDecimals = ((DecimalVarType)this.ResultType).decimals; if (srcDecimals == dstDecimals) { return(reg); } else if (srcDecimals < dstDecimals) { var diff = (dstDecimals - srcDecimals); var mult = (int)Math.Pow(10, diff); output.AppendLine(this, $"LOAD r0 {mult}"); output.AppendLine(this, $"MUL {reg} r0 {reg}"); return(reg); } else { throw new CompilerException($"Decimal precision failure: {expr.ResultType} => {this.ResultType}"); } } default: throw new CompilerException($"Unsupported cast: {expr.ResultType} => {this.ResultType}"); } } } var vmType = MethodInterface.ConvertType(ResultType); output.AppendLine(this, $"CAST {reg} {reg} #{vmType}"); return(reg); }
public MethodInterface AddMethod(string name, MethodImplementationType convention, VarType returnType, MethodParameter[] parameters, string alias = null) { if (!returnType.IsWeird && Compiler.Instance != null) { var vmType = MethodInterface.ConvertType(returnType); if (!ValidationUtils.IsValidMethod(name, vmType)) { throw new CompilerException("invalid method name: " + name); } } var method = new MethodInterface(this, convention, name, true, MethodKind.Method, returnType, parameters, alias); methods[name] = method; return(method); }
public override Register GenerateCode(CodeGenerator output) { var reg = Compiler.Instance.AllocRegister(output, this, this.NodeID); string val; switch (this.type.Kind) { case VarKind.Decimal: { var decType = this.type as DecimalVarType; var temp = decimal.Parse(this.value, CultureInfo.InvariantCulture); val = UnitConversion.ToBigInteger(temp, decType.decimals).ToString(); break; } case VarKind.Enum: { val = $"{this.value} Enum"; break; } case VarKind.Type: { var srcType = (VarKind)Enum.Parse(typeof(VarKind), this.value); var vmType = MethodInterface.ConvertType(srcType); val = $"{(int)vmType} Enum"; break; } default: { val = this.value; break; } } output.AppendLine(this, $"LOAD {reg} {val}"); this.CallNecessaryConstructors(output, type, reg); return(reg); }
public MethodInterface Clone(LibraryDeclaration targetLibrary) { var method = this; var parameters = new List <MethodParameter>(); foreach (var parameter in method.Parameters) { var entry = new MethodParameter(parameter.Name, parameter.Type); entry.Callback = parameter.Callback; parameters.Add(entry); } var newMethod = new MethodInterface(targetLibrary, method.Implementation, method.Name, method.IsPublic, method.Kind, method.ReturnType, parameters.ToArray(), method.Alias); newMethod.Contract = method.Contract; newMethod.PreCallback = method.PreCallback; newMethod.PostCallback = method.PostCallback; return(newMethod); }
public static LibraryDeclaration LoadLibrary(string name, Scope scope, ModuleKind moduleKind) { if (name != name.UppercaseFirst() && name != "this") { throw new CompilerException("invalid library name: " + name); } var libDecl = new LibraryDeclaration(scope, name); VarKind libKind; if (Enum.TryParse <VarKind>(name, out libKind) && libKind != VarKind.Bytes && libKind != VarKind.Method && libKind != VarKind.Task) { switch (libKind) { case VarKind.Decimal: case VarKind.Struct: case VarKind.Module: libKind = VarKind.Any; break; } libDecl.AddMethod("toBytes", MethodImplementationType.Custom, VarKind.Bytes, new[] { new MethodParameter("target", libKind) }). SetPreCallback((output, scope, expr) => { var reg = expr.arguments[0].GenerateCode(output); output.AppendLine(expr, $"CAST {reg} {reg} #{VMType.Bytes}"); return(reg); }); } switch (name) { case "Module": // TODO implementations of those libDecl.AddMethod("script", MethodImplementationType.Custom, VarKind.Bytes, new[] { new MethodParameter("target", VarKind.Module) }); libDecl.AddMethod("abi", MethodImplementationType.Custom, VarKind.Bytes, new[] { new MethodParameter("target", VarKind.Module) }); return(libDecl); case "Struct": libDecl.AddMethod("fromBytes", MethodImplementationType.Custom, VarKind.Struct, new[] { new MethodParameter("source", VarKind.Bytes) }); return(libDecl); case "String": libDecl.AddMethod("length", MethodImplementationType.Custom, VarKind.Number, new[] { new MethodParameter("target", VarKind.String) }). SetPreCallback((output, scope, expr) => { var reg = expr.arguments[0].GenerateCode(output); output.AppendLine(expr, $"SIZE {reg} {reg}"); return(reg); }); return(libDecl); case "Bytes": libDecl.AddMethod("toString", MethodImplementationType.Custom, VarKind.String, new[] { new MethodParameter("target", VarKind.Bytes) }). SetPreCallback((output, scope, expr) => { var reg = expr.arguments[0].GenerateCode(output); output.AppendLine(expr, $"CAST {reg} {reg} #{VMType.String}"); return(reg); }); return(libDecl); case "Enum": libDecl.AddMethod("isSet", MethodImplementationType.Custom, VarKind.Bool, new[] { new MethodParameter("target", VarKind.Enum), new MethodParameter("flag", VarKind.Enum) }). SetPreCallback((output, scope, expr) => { var regA = expr.arguments[0].GenerateCode(output); var regB = expr.arguments[1].GenerateCode(output); output.AppendLine(expr, $"AND {regA} {regB} {regA}"); Compiler.Instance.DeallocRegister(ref regB); return(regA); }); return(libDecl); case "Decimal": libDecl.AddMethod("decimals", MethodImplementationType.Custom, VarKind.Number, new[] { new MethodParameter("target", VarKind.Any) }). SetPreCallback((output, scope, expr) => { var reg = Compiler.Instance.AllocRegister(output, expr); var arg = expr.arguments[0]; var decType = (DecimalVarType)arg.ResultType; output.AppendLine(expr, $"LOAD {reg} {decType.decimals}"); return(reg); }); return(libDecl); case "Address": // TODO implementations of those libDecl.AddMethod("isNull", MethodImplementationType.Custom, VarKind.Bool, new[] { new MethodParameter("target", VarKind.Address) }); libDecl.AddMethod("isUser", MethodImplementationType.Custom, VarKind.Bool, new[] { new MethodParameter("target", VarKind.Address) }); libDecl.AddMethod("isSystem", MethodImplementationType.Custom, VarKind.Bool, new[] { new MethodParameter("target", VarKind.Address) }); libDecl.AddMethod("isInterop", MethodImplementationType.Custom, VarKind.Bool, new[] { new MethodParameter("target", VarKind.Address) }); libDecl.AddMethod("toString", MethodImplementationType.Custom, VarKind.String, new[] { new MethodParameter("target", VarKind.Address) }). SetPreCallback((output, scope, expr) => { var reg = expr.arguments[0].GenerateCode(output); output.AppendLine(expr, $"CAST {reg} {reg} #{VMType.String}"); return(reg); }); return(libDecl); } if (moduleKind == ModuleKind.Description) { switch (name) { case FormatLibraryName: libDecl.AddMethod("decimals", MethodImplementationType.ExtCall, VarKind.String, new[] { new MethodParameter("value", VarKind.Number), new MethodParameter("symbol", VarKind.String) }); libDecl.AddMethod("symbol", MethodImplementationType.ExtCall, VarKind.String, new[] { new MethodParameter("symbol", VarKind.String) }); libDecl.AddMethod("account", MethodImplementationType.ExtCall, VarKind.String, new[] { new MethodParameter("address", VarKind.Address) }); break; default: throw new CompilerException("unknown library: " + name); } return(libDecl); } switch (name) { case "Call": libDecl.AddMethod("interop", MethodImplementationType.ExtCall, VarType.Generic(0), new[] { new MethodParameter("method", VarKind.String), new MethodParameter("...", VarKind.Any) }); libDecl.AddMethod("contract", MethodImplementationType.ContractCall, VarType.Generic(0), new[] { new MethodParameter("contract", VarKind.String), new MethodParameter("method", VarKind.String), new MethodParameter("...", VarKind.Any) }); libDecl.AddMethod("method", MethodImplementationType.Custom, VarType.Generic(0), new[] { new MethodParameter("method", VarKind.Method), new MethodParameter("...", VarKind.Any) }); break; case "Chain": { libDecl.AddMethod("create", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("source", VarKind.Address), new MethodParameter("organization", VarKind.String), new MethodParameter("name", VarKind.String), new MethodParameter("parentName", VarKind.String) }).SetAlias("Nexus.CreateChain"); break; } case "Cryptography": { libDecl.AddMethod("AESDecrypt", MethodImplementationType.ExtCall, VarKind.Bytes, new[] { new MethodParameter("data", VarKind.Bytes), new MethodParameter("key", VarKind.Bytes) }).SetAlias("Runtime.AESDecrypt"); libDecl.AddMethod("AESEncrypt", MethodImplementationType.ExtCall, VarKind.Bytes, new[] { new MethodParameter("data", VarKind.Bytes), new MethodParameter("key", VarKind.Bytes) }).SetAlias("Runtime.AESEncrypt"); break; } case "Platform": { libDecl.AddMethod("create", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("source", VarKind.Address), new MethodParameter("name", VarKind.String), new MethodParameter("externalAddress", VarKind.String), new MethodParameter("interopAddress", VarKind.Address), new MethodParameter("symbol", VarKind.String) }).SetAlias("Nexus.CreatePlatform"); libDecl.AddMethod("setTokenHash", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("symbol", VarKind.String), new MethodParameter("platform", VarKind.String), new MethodParameter("hash", VarKind.Bytes) }).SetAlias("Nexus.SetTokenPlatformHash"); break; } case "Runtime": libDecl.AddMethod("expect", MethodImplementationType.Custom, VarKind.None, new[] { new MethodParameter("condition", VarKind.Bool), new MethodParameter("error", VarKind.String) }). SetPreCallback((output, scope, expr) => { var reg = expr.arguments[0].GenerateCode(output); output.AppendLine(expr, $"JMPIF {reg} @expect_{expr.NodeID}"); var reg2 = expr.arguments[1].GenerateCode(output); output.AppendLine(expr, $"THROW {reg2}"); Compiler.Instance.DeallocRegister(ref reg2); output.AppendLine(expr, $"@expect_{expr.NodeID}: NOP"); return(reg); }); libDecl.AddMethod("log", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("message", VarKind.String) }); libDecl.AddMethod("isWitness", MethodImplementationType.ExtCall, VarKind.Bool, new[] { new MethodParameter("address", VarKind.Address) }); libDecl.AddMethod("isTrigger", MethodImplementationType.ExtCall, VarKind.Bool, new MethodParameter[] { }); libDecl.AddMethod("transactionHash", MethodImplementationType.ExtCall, VarKind.Hash, new MethodParameter[] { }); libDecl.AddMethod("deployContract", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("name", VarKind.String), new MethodParameter("contract", VarKind.Module) }).SetParameterCallback("contract", ConvertFieldToContract); libDecl.AddMethod("upgradeContract", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("name", VarKind.String), new MethodParameter("contract", VarKind.Module) }).SetParameterCallback("contract", ConvertFieldToContract); libDecl.AddMethod("gasTarget", MethodImplementationType.ExtCall, VarKind.Address, new MethodParameter[] { }).SetAlias("Runtime.GasTarget"); libDecl.AddMethod("context", MethodImplementationType.ExtCall, VarKind.String, new MethodParameter[] { }).SetAlias("Runtime.Context"); break; case "Task": libDecl.AddMethod("start", MethodImplementationType.ExtCall, VarKind.Task, new MethodParameter[] { new MethodParameter("method", VarKind.Method), new MethodParameter("from", VarKind.Address), new MethodParameter("frequency", VarKind.Number), new MethodParameter("mode", VarType.Find(VarKind.Enum, "TaskMode")), new MethodParameter("gasLimit", VarKind.Number) }).SetAlias("Task.Start"); libDecl.AddMethod("stop", MethodImplementationType.ExtCall, VarKind.None, new MethodParameter[] { new MethodParameter("task", VarKind.Address) }).SetAlias("Task.Stop"); libDecl.AddMethod("current", MethodImplementationType.ExtCall, VarKind.Task, new MethodParameter[] { }).SetAlias("Task.Current"); break; case "Time": libDecl.AddMethod("now", MethodImplementationType.ExtCall, VarKind.Timestamp, new MethodParameter[] { }).SetAlias("Runtime.Time"); libDecl.AddMethod("unix", MethodImplementationType.Custom, VarKind.Timestamp, new[] { new MethodParameter("value", VarKind.Number) }).SetPostCallback((output, scope, method, reg) => { var nameExpr = method.arguments[0] as LiteralExpression; if (nameExpr != null && nameExpr.type.Kind == VarKind.Number) { var timestamp = uint.Parse(nameExpr.value); output.AppendLine(method, $"LOAD {reg} {timestamp}"); method.CallNecessaryConstructors(output, VarKind.Timestamp, reg); return(reg); } else { throw new Exception("Expected literal number expression"); } }); break; case "UID": { libDecl.AddMethod("generate", MethodImplementationType.ExtCall, VarKind.Number, new MethodParameter[] { }).SetAlias("Runtime.GenerateUID"); break; } case "Random": libDecl.AddMethod("generate", MethodImplementationType.ExtCall, VarKind.Number, new MethodParameter[] { }).SetAlias("Runtime.Random"); libDecl.AddMethod("seed", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("seed", VarKind.Number) }).SetAlias("Runtime.SetSeed"); break; case "Token": libDecl.AddMethod("create", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("name", VarKind.String), new MethodParameter("maxSupply", VarKind.Number), new MethodParameter("decimals", VarKind.Number), new MethodParameter("flags", VarKind.Number), new MethodParameter("script", VarKind.Bytes) }).SetAlias("Nexus.CreateToken"); libDecl.AddMethod("exists", MethodImplementationType.ExtCall, VarKind.Bool, new[] { new MethodParameter("symbol", VarKind.String) }).SetAlias("Runtime.TokenExists"); libDecl.AddMethod("getDecimals", MethodImplementationType.ExtCall, VarKind.Number, new[] { new MethodParameter("symbol", VarKind.String) }).SetAlias("Runtime.GetTokenDecimals"); libDecl.AddMethod("getFlags", MethodImplementationType.ExtCall, VarType.Find(VarKind.Enum, "TokenFlag"), new[] { new MethodParameter("symbol", VarKind.String) }).SetAlias("Runtime.GetTokenFlags"); libDecl.AddMethod("transfer", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("to", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("amount", VarKind.Number) }).SetAlias("Runtime.TransferTokens"); libDecl.AddMethod("transferAll", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("to", VarKind.Address), new MethodParameter("symbol", VarKind.String) }).SetAlias("Runtime.TransferBalance"); libDecl.AddMethod("mint", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("to", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("amount", VarKind.Number) }).SetAlias("Runtime.MintTokens"); libDecl.AddMethod("burn", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("amount", VarKind.Number) }).SetAlias("Runtime.BurnTokens"); libDecl.AddMethod("swap", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("targetChain", VarKind.String), new MethodParameter("source", VarKind.Address), new MethodParameter("destination", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("amount", VarKind.Number) }).SetAlias("Runtime.SwapTokens"); libDecl.AddMethod("getBalance", MethodImplementationType.ExtCall, VarKind.Number, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("symbol", VarKind.String) }).SetAlias("Runtime.GetBalance"); libDecl.AddMethod("isMinter", MethodImplementationType.ExtCall, VarKind.Bool, new[] { new MethodParameter("address", VarKind.Address), new MethodParameter("symbol", VarKind.String) }).SetAlias("Runtime.IsMinter"); break; case "NFT": libDecl.AddMethod("transfer", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("to", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("id", VarKind.Number) }).SetAlias("Runtime.TransferToken"); libDecl.AddMethod("mint", MethodImplementationType.ExtCall, VarKind.Number, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("to", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("rom", VarKind.Any), new MethodParameter("ram", VarKind.Any), new MethodParameter("seriesID", VarKind.Any) }) .SetParameterCallback("rom", ConvertFieldToBytes).SetParameterCallback("ram", ConvertFieldToBytes).SetAlias("Runtime.MintToken"); libDecl.AddMethod("readROM", MethodImplementationType.ExtCall, VarType.Generic(0), new[] { new MethodParameter("symbol", VarKind.String), new MethodParameter("id", VarKind.Number) }).SetAlias("Runtime.ReadTokenROM").SetPostCallback(ConvertGenericResult); libDecl.AddMethod("readRAM", MethodImplementationType.ExtCall, VarType.Generic(0), new[] { new MethodParameter("symbol", VarKind.String), new MethodParameter("id", VarKind.Number) }).SetAlias("Runtime.ReadTokenRAM").SetPostCallback(ConvertGenericResult); libDecl.AddMethod("burn", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("id", VarKind.Number) }).SetAlias("Runtime.BurnToken"); libDecl.AddMethod("infuse", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("id", VarKind.Number), new MethodParameter("infuseSymbol", VarKind.String), new MethodParameter("infuseValue", VarKind.Number) }).SetAlias("Runtime.InfuseToken"); libDecl.AddMethod("createSeries", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("seriesID", VarKind.Number), new MethodParameter("maxSupply", VarKind.Number), new MethodParameter("mode", VarType.Find(VarKind.Enum, "TokenSeries")), new MethodParameter("nft", VarKind.Module) }). SetAlias("Nexus.CreateTokenSeries").SetParameterCallback("nft", ConvertFieldToContract); libDecl.AddMethod("read", MethodImplementationType.ExtCall, VarType.Find(VarKind.Struct, "NFT"), new[] { new MethodParameter("symbol", VarKind.String), new MethodParameter("id", VarKind.Number) }).SetAlias("Runtime.ReadToken") .SetPreCallback((output, scope, expr) => { var nftStruct = VarType.Find(VarKind.Struct, "NFT") as StructVarType; var reg = Compiler.Instance.AllocRegister(output, expr); var fields = '\"' + string.Join(',', nftStruct.decl.fields.Select(x => x.name)) + '\"'; output.AppendLine(expr, $"LOAD {reg} {fields} // field list"); output.AppendLine(expr, $"PUSH {reg}"); return(reg); }) .SetPostCallback((output, scope, method, reg) => { output.AppendLine(method, $"UNPACK {reg} {reg}"); return(reg); }); break; case "Organization": { libDecl.AddMethod("create", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("id", VarKind.String), new MethodParameter("name", VarKind.String), new MethodParameter("script", VarKind.Bytes) }).SetAlias("Nexus.CreateOrganization"); libDecl.AddMethod("addMember", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("name", VarKind.String), new MethodParameter("target", VarKind.Address) }); break; } case "Oracle": { libDecl.AddMethod("read", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("url", VarKind.String) }); libDecl.AddMethod("price", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("symbol", VarKind.String) }); libDecl.AddMethod("quote", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("baseSymbol", VarKind.String), new MethodParameter("quoteSymbol", VarKind.String), new MethodParameter("amount", VarKind.Number) }); break; } case "Storage": { libDecl.AddMethod("read", MethodImplementationType.ExtCall, VarKind.Any, new[] { new MethodParameter("contract", VarKind.String), new MethodParameter("field", VarKind.String), new MethodParameter("type", VarKind.Number) }).SetAlias("Data.Get"); libDecl.AddMethod("write", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("field", VarKind.String), new MethodParameter("value", VarKind.Any) }).SetAlias("Data.Set"); libDecl.AddMethod("delete", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("field", VarKind.String) }).SetAlias("Data.Delete"); break; } case "Utils": libDecl.AddMethod("contractAddress", MethodImplementationType.Custom, VarKind.Address, new[] { new MethodParameter("name", VarKind.String) }).SetPostCallback((output, scope, method, reg) => { var nameExpr = method.arguments[0] as LiteralExpression; if (nameExpr != null && nameExpr.type.Kind == VarKind.String) { var address = SmartContract.GetAddressForName(nameExpr.value); var hex = Base16.Encode(address.ToByteArray()); output.AppendLine(method, $"LOAD {reg} 0x{hex}"); return(reg); } else { throw new Exception("Expected literal string expression"); } }); break; case "Leaderboard": { var contract = NativeContractKind.Ranking.ToString().ToLower(); libDecl.AddMethod("create", MethodImplementationType.ContractCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("boardName", VarKind.String), new MethodParameter("capacity", VarKind.Number) }).SetContract(contract).SetAlias(nameof(RankingContract.CreateLeaderboard)); libDecl.AddMethod("getAddress", MethodImplementationType.ContractCall, VarKind.Address, new[] { new MethodParameter("boardName", VarKind.String), new MethodParameter("index", VarKind.Number) }).SetContract(contract).SetAlias(nameof(RankingContract.GetAddressByIndex)); libDecl.AddMethod("getScoreByIndex", MethodImplementationType.ContractCall, VarKind.Number, new[] { new MethodParameter("boardName", VarKind.String), new MethodParameter("index", VarKind.Number) }).SetContract(contract).SetAlias(nameof(RankingContract.GetScoreByIndex)); libDecl.AddMethod("getScoreByAddress", MethodImplementationType.ContractCall, VarKind.Number, new[] { new MethodParameter("boardName", VarKind.String), new MethodParameter("target", VarKind.Address) }).SetContract(contract).SetAlias(nameof(RankingContract.GetScoreByAddress)); libDecl.AddMethod("getSize", MethodImplementationType.ContractCall, VarKind.Number, new[] { new MethodParameter("boardName", VarKind.String) }).SetContract(contract).SetAlias(nameof(RankingContract.GetSize)); libDecl.AddMethod("insert", MethodImplementationType.ContractCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("target", VarKind.Address), new MethodParameter("boardName", VarKind.String), new MethodParameter("score", VarKind.Number) }).SetContract(contract).SetAlias(nameof(RankingContract.InsertScore)); libDecl.AddMethod("reset", MethodImplementationType.ContractCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("boardName", VarKind.String) }).SetContract(contract).SetAlias(nameof(RankingContract.ResetLeaderboard)); break; } case "Market": { var contract = NativeContractKind.Market.ToString().ToLower(); libDecl.AddMethod("sell", MethodImplementationType.ContractCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("baseSymbol", VarKind.String), new MethodParameter("quoteSymbol", VarKind.String), new MethodParameter("tokenID", VarKind.Number), new MethodParameter("price", VarKind.Number), new MethodParameter("endDate", VarKind.Timestamp) }).SetContract(contract).SetAlias(nameof(MarketContract.SellToken)); libDecl.AddMethod("buy", MethodImplementationType.ContractCall, VarKind.None, new[] { new MethodParameter("from", VarKind.Address), new MethodParameter("symbol", VarKind.String), new MethodParameter("tokenID", VarKind.Number) }).SetContract(contract).SetAlias(nameof(MarketContract.BuyToken)); libDecl.AddMethod("cancel", MethodImplementationType.ContractCall, VarKind.None, new[] { new MethodParameter("symbol", VarKind.String), new MethodParameter("tokenID", VarKind.Number) }).SetContract(contract).SetAlias(nameof(MarketContract.CancelSale)); libDecl.AddMethod("hasAuction", MethodImplementationType.ContractCall, VarKind.Bool, new[] { new MethodParameter("symbol", VarKind.String), new MethodParameter("tokenID", VarKind.Number) }).SetContract(contract).SetAlias(nameof(MarketContract.HasAuction)); break; } case "Map": libDecl.AddMethod("get", MethodImplementationType.ExtCall, VarType.Generic(1), new[] { new MethodParameter("map", VarKind.String), new MethodParameter("key", VarType.Generic(0)) }).SetParameterCallback("map", ConvertFieldToStorageAccessRead) .SetPreCallback((output, scope, expr) => { var vmType = MethodInterface.ConvertType(expr.method.ReturnType); var reg = Compiler.Instance.AllocRegister(output, expr); output.AppendLine(expr, $"LOAD {reg} {(int)vmType} // field type"); output.AppendLine(expr, $"PUSH {reg}"); return(reg); }) .SetPostCallback(ConvertGenericResult); libDecl.AddMethod("set", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("map", VarKind.String), new MethodParameter("key", VarType.Generic(0)), new MethodParameter("value", VarType.Generic(1)) }).SetParameterCallback("map", ConvertFieldToStorageAccessWrite); libDecl.AddMethod("remove", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("map", VarKind.String), new MethodParameter("key", VarType.Generic(0)) }).SetParameterCallback("map", ConvertFieldToStorageAccessWrite); libDecl.AddMethod("clear", MethodImplementationType.ExtCall, VarKind.None, new[] { new MethodParameter("map", VarKind.String) }).SetParameterCallback("map", ConvertFieldToStorageAccessWrite); libDecl.AddMethod("count", MethodImplementationType.ExtCall, VarKind.Number, new[] { new MethodParameter("map", VarKind.String) }).SetParameterCallback("map", ConvertFieldToStorageAccessRead); libDecl.AddMethod("has", MethodImplementationType.ExtCall, VarKind.Bool, new[] { new MethodParameter("map", VarKind.String), new MethodParameter("key", VarType.Generic(0)) }).SetParameterCallback("map", ConvertFieldToStorageAccessRead) .SetPreCallback((output, scope, expr) => { var vmType = MethodInterface.ConvertType(expr.method.ReturnType); var reg = Compiler.Instance.AllocRegister(output, expr); output.AppendLine(expr, $"LOAD {reg} {(int)vmType} // field type"); output.AppendLine(expr, $"PUSH {reg}"); return(reg); }); break; case "List": libDecl.AddMethod("get", MethodImplementationType.Custom, VarType.Generic(0), new[] { new MethodParameter("list", VarKind.String), new MethodParameter("index", VarKind.Number) }); libDecl.AddMethod("add", MethodImplementationType.Custom, VarKind.None, new[] { new MethodParameter("list", VarKind.String), new MethodParameter("value", VarType.Generic(0)) }); libDecl.AddMethod("replace", MethodImplementationType.Custom, VarKind.None, new[] { new MethodParameter("list", VarKind.String), new MethodParameter("index", VarKind.Number), new MethodParameter("value", VarType.Generic(0)) }); libDecl.AddMethod("remove", MethodImplementationType.Custom, VarKind.None, new[] { new MethodParameter("list", VarKind.String), new MethodParameter("index", VarKind.Number) }); libDecl.AddMethod("count", MethodImplementationType.Custom, VarKind.Number, new[] { new MethodParameter("list", VarKind.String) }); libDecl.AddMethod("clear", MethodImplementationType.Custom, VarKind.None, new[] { new MethodParameter("list", VarKind.String) }); break; default: throw new CompilerException("unknown library: " + name); } return(libDecl); }
public ReturnStatement(MethodInterface method, Expression expression) : base() { this.expression = expression; this.method = method; }
public void GenerateCode(CodeGenerator output) { output.AppendLine(this); output.AppendLine(this, $"// ********* {this.Name} {[email protected]} ***********"); output.AppendLine(this, $"@{GetEntryLabel()}:"); [email protected] = output.LineCount; Register tempReg1 = null; Register tempReg2 = null; bool isConstructor = [email protected] == MethodKind.Constructor; // here we generate code that runs at the entry point of this method // we need to fetch the global variables from storage and allocate registers for them foreach (var variable in this.scope.Parent.Variables.Values) { if (variable.Storage != VarStorage.Global) { if (variable.Storage == VarStorage.NFT && this.scope.Module.Kind != ModuleKind.NFT) { throw new CompilerException($"implicit variable '{variable.Name}' not allowed in {this.scope.Module.Kind}"); } continue; } if (!this.IsNodeUsed(variable)) { variable.Register = null; continue; } if (tempReg1 == null && !isConstructor) { tempReg1 = Compiler.Instance.AllocRegister(output, this, "dataGet"); output.AppendLine(this, $"LOAD {tempReg1} \"Data.Get\""); tempReg2 = Compiler.Instance.AllocRegister(output, this, "contractName"); output.AppendLine(this, $"LOAD {tempReg2} \"{this.scope.Module.Name}\""); } var reg = Compiler.Instance.AllocRegister(output, variable, variable.Name); variable.Register = reg; if (isConstructor) { continue; // in a constructor we don't need to read the vars from storage as they dont exist yet } //var fieldKey = SmartContract.GetKeyForField(this.scope.Root.Name, variable.Name, false); VM.VMType vmType = MethodInterface.ConvertType(variable.Type); output.AppendLine(this, $"// reading global: {variable.Name}"); output.AppendLine(this, $"LOAD r0 {(int)vmType}"); output.AppendLine(this, $"PUSH r0"); output.AppendLine(this, $"LOAD r0 \"{variable.Name}\""); output.AppendLine(this, $"PUSH r0"); output.AppendLine(this, $"PUSH {tempReg2}"); output.AppendLine(this, $"EXTCALL {tempReg1}"); output.AppendLine(this, $"POP {reg}"); variable.CallNecessaryConstructors(output, variable.Type, reg); } var implicits = new List <VarDeclaration>(); if (this.scope.Module.Kind == ModuleKind.NFT) { var idReg = Compiler.Instance.AllocRegister(output, this); output.AppendLine(this, $"POP {idReg} // get nft tokenID from stack"); implicits = this.scope.Parent.Variables.Values.Where(x => x.Storage == VarStorage.NFT && this.IsNodeUsed(x) && !x.Name.Equals("_tokenID", StringComparison.OrdinalIgnoreCase)).ToList(); VarDeclaration tokenIDVar = this.scope.Parent.Variables.Values.Where(x => x.Storage == VarStorage.NFT && this.IsNodeUsed(x) && x.Name.Equals("_tokenID", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (implicits.Count > 0) { var fieldStr = string.Join(',', implicits.Select(x => x.Name.Substring(1))); output.AppendLine(this, $"// reading nft data"); output.AppendLine(this, $"LOAD r0 \"" + fieldStr + "\""); output.AppendLine(this, $"PUSH r0 // fields"); output.AppendLine(this, $"PUSH {idReg} // tokenID"); output.AppendLine(this, $"LOAD r0 \"" + this.scope.Module.Parent.Name + "\""); output.AppendLine(this, $"PUSH r0 // symbol"); output.AppendLine(this, $"LOAD r0 \"Runtime.ReadToken\""); output.AppendLine(this, $"EXTCALL r0"); var dataReg = Compiler.Instance.AllocRegister(output, this); output.AppendLine(this, $"POP {dataReg}"); output.AppendLine(this, $"UNPACK {dataReg} {dataReg}"); foreach (var variable in implicits) { var fieldName = variable.Name.Substring(1); var reg = Compiler.Instance.AllocRegister(output, variable, variable.Name); variable.Register = reg; output.AppendLine(this, $"LOAD r0 \"{fieldName}\""); output.AppendLine(this, $"GET {dataReg} {reg} r0"); if (variable.Type.Kind == VarKind.Struct) { output.AppendLine(this, $"UNPACK {reg} {reg}"); } } Compiler.Instance.DeallocRegister(ref dataReg); } if (tokenIDVar != null) { var reg = Compiler.Instance.AllocRegister(output, tokenIDVar, tokenIDVar.Name); tokenIDVar.Register = reg; output.AppendLine(this, $"COPY {idReg} {reg} // tokenID"); implicits.Add(tokenIDVar); } Compiler.Instance.DeallocRegister(ref idReg); } Compiler.Instance.DeallocRegister(ref tempReg1); Compiler.Instance.DeallocRegister(ref tempReg2); foreach (var variable in this.scope.Variables.Values) { if (variable.Storage != VarStorage.Argument) { continue; } variable.Register = Compiler.Instance.AllocRegister(output, variable, variable.Name); output.AppendLine(this, $"POP {variable.Register}"); switch (variable.Type.Kind) { // TODO this fixes numbers passed as strings, but maybe other types would benefit from this... case VarKind.Number: output.AppendLine(this, $"CAST {variable.Register} {variable.Register} #Number"); break; } variable.CallNecessaryConstructors(output, variable.Type, variable.Register); } this.scope.Enter(output); body.GenerateCode(output); this.scope.Leave(output); foreach (var variable in this.scope.Variables.Values) { if (variable.Storage != VarStorage.Argument) { continue; } Compiler.Instance.DeallocRegister(ref variable.Register); } foreach (var variable in implicits) { Compiler.Instance.DeallocRegister(ref variable.Register); } // NOTE we don't need to dealloc anything here besides the global vars foreach (var variable in this.scope.Parent.Variables.Values) { if (variable.Storage != VarStorage.Global) { continue; } if (variable.Register == null) { if (isConstructor && !variable.Type.IsStorageBound) { throw new CompilerException($"global variable '{variable.Name}' not assigned in constructor of {this.scope.Module.Name}"); } continue; // if we hit this, means it went unused } bool isAssigned = false; this.body.Visit((node) => { var assignement = node as AssignStatement; if (assignement != null && assignement.variable == variable) { isAssigned = true; } }); // if the global variable is not assigned within the current method, no need to save it value back to the storage if (isAssigned) { if (tempReg1 == null) { tempReg1 = Compiler.Instance.AllocRegister(output, this); output.AppendLine(this, $"LOAD {tempReg1} \"Data.Set\""); } // NOTE we could keep this key loaded in a register if we had enough spare registers.. output.AppendLine(this, $"// writing global: {variable.Name}"); output.AppendLine(this, $"PUSH {variable.Register}"); output.AppendLine(this, $"LOAD r0 \"{variable.Name}\""); output.AppendLine(this, $"PUSH r0"); output.AppendLine(this, $"EXTCALL {tempReg1}"); } if (variable.Register != null) { Compiler.Instance.DeallocRegister(ref variable.Register); } } Compiler.Instance.DeallocRegister(ref tempReg1); output.AppendLine(this, "RET"); [email protected] = output.LineCount; var returnType = [email protected]; if (returnType != VarKind.None) { // TODO validate if all possible paths have a return bool hasReturn = false; this.body.Visit((node) => { if (node is ReturnStatement) { hasReturn = true; } }); if (!hasReturn) { throw new CompilerException($"not all paths return a value of type {returnType}"); } } }
public MethodDeclaration(Scope scope, MethodInterface @interface) : base(scope.Parent, @interface.Name) { this.body = null; this.scope = scope; this.@interface = @interface; }
internal ContractEvent GetABI() { var type = MethodInterface.ConvertType(this.returnType); return(new ContractEvent(this.value, this.Name, type, descriptionScript)); }
public void PatchGenerics() { int requiredGenerics = 0; this.method = this.method.Clone(this.method.Library); // auto patch storage methods if (this.generics.Count == 0) { var genericLib = this.method.Library as GenericLibraryDeclaration; if (genericLib != null) { foreach (var type in genericLib.Generics) { this.generics.Add(type); } } } if (ResultType.Kind == VarKind.Generic) { var generic = (GenericVarType)ResultType; if (generic.index < 0) { throw new CompilerException($"weird generic index for return type of method {this.method.Name}, compiler bug?"); } if (generic.index >= this.generics.Count) { throw new CompilerException($"missing generic declaration with index {generic.index} when calling method {this.method.Name}"); } requiredGenerics = Math.Max(requiredGenerics, generic.index + 1); this.method.ReturnType = this.generics[generic.index]; } for (int paramIndex = 0; paramIndex < this.method.Parameters.Length; paramIndex++) { var parameter = this.method.Parameters[paramIndex]; if (parameter.Type.Kind == VarKind.Generic) { var generic = (GenericVarType)parameter.Type; if (generic.index < 0) { throw new CompilerException($"weird generic index for parameter {parameter.Name} of method {this.method.Name}, compiler bug?"); } if (generic.index >= this.generics.Count) { throw new CompilerException($"missing generic declaration with index {generic.index} when calling method {this.method.Name}"); } requiredGenerics = Math.Max(requiredGenerics, generic.index + 1); this.method.Parameters[paramIndex] = new MethodParameter(parameter.Name, this.generics[generic.index]); } } if (requiredGenerics > generics.Count) { throw new CompilerException($"call to method {this.method.Name} expected {requiredGenerics} generics, got {generics.Count} instead"); } }