Beispiel #1
0
        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);
        }
Beispiel #2
0
        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()));
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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}");
                }
            }
        }
Beispiel #8
0
        internal ContractEvent GetABI()
        {
            var type = MethodInterface.ConvertType(this.returnType);

            return(new ContractEvent(this.value, this.Name, type, descriptionScript));
        }