/* This file is part of TON Blockchain source code. TON Blockchain is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. TON Blockchain is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with TON Blockchain. If not, see . */ #include "tolk.h" #include "platform-utils.h" #include "src-file.h" #include "ast.h" #include "compiler-state.h" #include "constant-evaluator.h" #include "generics-helpers.h" #include "td/utils/crypto.h" #include "type-system.h" #include /* * This pipe registers global symbols: functions, constants, global vars, etc. * It happens just after all files have been parsed to AST. * * "Registering" means adding symbols to a global symbol table. * After this pass, any global symbol can be looked up. * Note, that local variables are not analyzed here, it's a later step. * Before digging into locals, we need a global symtable to be filled, exactly done here. */ namespace tolk { static int calculate_method_id_for_entrypoint(std::string_view func_name) { if (func_name == "main" || func_name == "onInternalMessage") { return 0; } if (func_name == "onExternalMessage") { return -1; } if (func_name == "onRunTickTock") { return -2; } if (func_name == "onSplitPrepare") { return -3; } if (func_name == "onSplitInstall") { return -4; } tolk_assert(false); } static int calculate_method_id_by_func_name(std::string_view func_name) { unsigned int crc = td::crc16(static_cast(func_name)); return static_cast(crc & 0xffff) | 0x10000; } static void validate_arg_ret_order_of_asm_function(V v_body, int n_params, TypePtr ret_type) { if (!ret_type) { v_body->error("asm function must declare return type (before asm instructions)"); } if (n_params > 16) { v_body->error("asm function can have at most 16 parameters"); } // asm(param1 ... paramN), param names were previously mapped into indices if (!v_body->arg_order.empty()) { if (static_cast(v_body->arg_order.size()) != n_params) { v_body->error("arg_order of asm function must specify all parameters"); } std::vector visited(v_body->arg_order.size(), false); for (int j : v_body->arg_order) { if (visited[j]) { v_body->error("arg_order of asm function contains duplicates"); } visited[j] = true; } } // asm(-> 0 2 1 3), check for a shuffled range 0...N // correctness of N (actual return width onto a stack) will be checked after type inferring and generics instantiation if (!v_body->ret_order.empty()) { std::vector visited(v_body->ret_order.size(), false); for (int j : v_body->ret_order) { if (j < 0 || j >= static_cast(v_body->ret_order.size()) || visited[j]) { v_body->error("ret_order contains invalid integer, not in range 0 .. N"); } visited[j] = true; } } } static const GenericsDeclaration* construct_genericTs(V v_list) { std::vector itemsT; itemsT.reserve(v_list->size()); for (int i = 0; i < v_list->size(); ++i) { auto v_item = v_list->get_item(i); auto it_existing = std::find_if(itemsT.begin(), itemsT.end(), [v_item](const GenericsDeclaration::GenericsItem& prev) { return prev.nameT == v_item->nameT; }); if (it_existing != itemsT.end()) { v_item->error("duplicate generic parameter `" + static_cast(v_item->nameT) + "`"); } itemsT.emplace_back(v_item->nameT); } return new GenericsDeclaration(std::move(itemsT)); } static void register_constant(V v) { ConstantValue init_value = eval_const_init_value(v->get_init_value()); GlobalConstData* c_sym = new GlobalConstData(static_cast(v->get_identifier()->name), v->loc, v->declared_type, std::move(init_value)); if (v->declared_type) { bool ok = (c_sym->is_int_const() && (v->declared_type == TypeDataInt::create())) || (c_sym->is_slice_const() && (v->declared_type == TypeDataSlice::create())); if (!ok) { v->error("expression type does not match declared type"); } } G.symtable.add_global_const(c_sym); G.all_constants.push_back(c_sym); v->mutate()->assign_const_ref(c_sym); } static void register_global_var(V v) { GlobalVarData* g_sym = new GlobalVarData(static_cast(v->get_identifier()->name), v->loc, v->declared_type); G.symtable.add_global_var(g_sym); G.all_global_vars.push_back(g_sym); v->mutate()->assign_var_ref(g_sym); } static LocalVarData register_parameter(V v, int idx) { if (v->is_underscore()) { return {"", v->loc, v->declared_type, 0, idx}; } int flags = 0; if (v->declared_as_mutate) { flags |= LocalVarData::flagMutateParameter; } if (!v->declared_as_mutate && idx == 0 && v->param_name == "self") { flags |= LocalVarData::flagImmutable; } return LocalVarData(static_cast(v->param_name), v->loc, v->declared_type, flags, idx); } static void register_function(V v) { std::string_view func_name = v->get_identifier()->name; // calculate TypeData of a function std::vector arg_types; std::vector parameters; int n_params = v->get_num_params(); int n_mutate_params = 0; arg_types.reserve(n_params); parameters.reserve(n_params); for (int i = 0; i < n_params; ++i) { auto v_param = v->get_param(i); arg_types.emplace_back(v_param->declared_type); parameters.emplace_back(register_parameter(v_param, i)); n_mutate_params += static_cast(v_param->declared_as_mutate); } const GenericsDeclaration* genericTs = nullptr; if (v->genericsT_list) { genericTs = construct_genericTs(v->genericsT_list); } if (v->is_builtin_function()) { const Symbol* builtin_func = lookup_global_symbol(func_name); const FunctionData* fun_ref = builtin_func ? builtin_func->as() : nullptr; if (!fun_ref || !fun_ref->is_builtin_function()) { v->error("`builtin` used for non-builtin function"); } v->mutate()->assign_fun_ref(fun_ref); return; } if (G.is_verbosity(1) && v->is_code_function()) { std::cerr << "fun " << func_name << " : " << v->declared_return_type << std::endl; } FunctionBody f_body = v->get_body()->type == ast_sequence ? static_cast(new FunctionBodyCode) : static_cast(new FunctionBodyAsm); FunctionData* f_sym = new FunctionData(static_cast(func_name), v->loc, v->declared_return_type, std::move(parameters), 0, genericTs, nullptr, f_body, v); if (const auto* v_asm = v->get_body()->try_as()) { validate_arg_ret_order_of_asm_function(v_asm, v->get_num_params(), v->declared_return_type); f_sym->arg_order = v_asm->arg_order; f_sym->ret_order = v_asm->ret_order; } if (v->method_id.not_null()) { f_sym->method_id = static_cast(v->method_id->to_long()); } else if (v->flags & FunctionData::flagGetMethod) { f_sym->method_id = calculate_method_id_by_func_name(func_name); for (const FunctionData* other : G.all_get_methods) { if (other->method_id == f_sym->method_id) { v->error(PSTRING() << "GET methods hash collision: `" << other->name << "` and `" << f_sym->name << "` produce the same hash. Consider renaming one of these functions."); } } } else if (v->flags & FunctionData::flagIsEntrypoint) { f_sym->method_id = calculate_method_id_for_entrypoint(func_name); } f_sym->flags |= v->flags; if (n_mutate_params) { f_sym->flags |= FunctionData::flagHasMutateParams; } G.symtable.add_function(f_sym); G.all_functions.push_back(f_sym); if (f_sym->is_get_method()) { G.all_get_methods.push_back(f_sym); } v->mutate()->assign_fun_ref(f_sym); } static void iterate_through_file_symbols(const SrcFile* file) { static std::unordered_set seen; if (!seen.insert(file).second) { return; } tolk_assert(file && file->ast); for (AnyV v : file->ast->as()->get_toplevel_declarations()) { switch (v->type) { case ast_import_directive: // on `import "another-file.tolk"`, register symbols from that file at first // (for instance, it can calculate constants, which are used in init_val of constants in current file below import) iterate_through_file_symbols(v->as()->file); break; case ast_constant_declaration: register_constant(v->as()); break; case ast_global_var_declaration: register_global_var(v->as()); break; case ast_function_declaration: register_function(v->as()); break; default: break; } } } void pipeline_register_global_symbols() { for (const SrcFile* file : G.all_src_files) { iterate_through_file_symbols(file); } } } // namespace tolk