From e2cca03a78c1632cfa6fb2e9dc37d05509ee6ccb Mon Sep 17 00:00:00 2001 From: AlexeyFSL <112364963+AlexeyFSL@users.noreply.github.com> Date: Wed, 14 Sep 2022 16:36:01 +0700 Subject: [PATCH] Func and Fift lib for WASM (#455) * Add social badges Add telegram, Twitter and Stack Overflow badges in Readme * update README.md badges * patch for wasm build * fix narrowing conversion error for clang compiler * refactor func code * funcfift lib implementation * fix funcfift lib fix CMakeFile * fix rvalue missing * remove unused field from result json * name fix remove unused target * rename * added script for building funcfiftlib to wasm * fix json fild names * fix commit hash for script * added version function to funcfiftlib * update commit hash for script * add realpath fail processing fix DISABLE_EXCEPTION_CATCHING option * update hash in script Co-authored-by: Anthony Tsivarev Co-authored-by: EmelyanenkoK Co-authored-by: tolya-yanot <1449561+tolya-yanot@users.noreply.github.com> --- .github/script/fift-func-wasm-build-ubuntu.sh | 79 +++++++ CMakeLists.txt | 12 +- crypto/CMakeLists.txt | 50 ++++- crypto/func/func-main.cpp | 127 +++++++++++ crypto/func/func.cpp | 201 ++++++------------ crypto/func/func.h | 15 ++ crypto/func/parse-func.cpp | 9 +- crypto/funcfiftlib/funcfiftlib.cpp | 131 ++++++++++++ crypto/vm/large-boc-serializer.cpp | 2 +- tdutils/td/utils/port/config.h | 8 +- tonlib/CMakeLists.txt | 20 +- 11 files changed, 491 insertions(+), 163 deletions(-) create mode 100755 .github/script/fift-func-wasm-build-ubuntu.sh create mode 100644 crypto/func/func-main.cpp create mode 100644 crypto/funcfiftlib/funcfiftlib.cpp diff --git a/.github/script/fift-func-wasm-build-ubuntu.sh b/.github/script/fift-func-wasm-build-ubuntu.sh new file mode 100755 index 00000000..505ce137 --- /dev/null +++ b/.github/script/fift-func-wasm-build-ubuntu.sh @@ -0,0 +1,79 @@ +# The script build funcfift compiler to WASM + +# dependencies: +#sudo apt-get install -y build-essential git make cmake clang libgflags-dev zlib1g-dev libssl-dev libreadline-dev libmicrohttpd-dev pkg-config libgsl-dev python3 python3-dev python3-pip nodejs + +export CC=$(which clang) +export CXX=$(which clang++) +export CCACHE_DISABLE=1 + +git clone https://github.com/openssl/openssl.git +cd openssl +git checkout OpenSSL_1_1_1j + +./config +make -j4 + +OPENSSL_DIR=`pwd` + +cd .. + +git clone https://github.com/madler/zlib.git +cd zlib +ZLIB_DIR=`pwd` + +cd .. + +# clone ton repo +git clone --recursive https://github.com/the-ton-tech/ton-blockchain.git + +# only to generate auto-block.cpp + +cd ton-blockchain +git pull +git checkout 1566a23b2bece49fd1de9ab2f35e88297d22829f +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=/usr/lib/x86_64-linux-gnu/libz.so -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.so -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.so .. +make -j4 fift + +rm -rf * + +cd ../.. + +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk +./emsdk install latest +./emsdk activate latest +EMSDK_DIR=`pwd` + +source $EMSDK_DIR/emsdk_env.sh +export CC=$(which emcc) +export CXX=$(which em++) +export CCACHE_DISABLE=1 + +cd ../zlib + +emconfigure ./configure --static +emmake make -j4 +ZLIB_DIR=`pwd` + +cd ../openssl + +make clean +emconfigure ./Configure linux-generic32 no-shared no-dso no-engine no-unit-test no-ui +sed -i 's/CROSS_COMPILE=.*/CROSS_COMPILE=/g' Makefile +sed -i 's/-ldl//g' Makefile +sed -i 's/-O3/-Os/g' Makefile +emmake make depend +emmake make -j4 + +cd ../ton-blockchain + +cd build + +emcmake cmake -DUSE_EMSCRIPTEN=ON -DCMAKE_BUILD_TYPE=Release -DZLIB_LIBRARY=$ZLIB_DIR/libz.a -DZLIB_INCLUDE_DIR=$ZLIB_DIR -DOPENSSL_ROOT_DIR=$OPENSSL_DIR -DOPENSSL_INCLUDE_DIR=$OPENSSL_DIR/include -DOPENSSL_CRYPTO_LIBRARY=$OPENSSL_DIR/libcrypto.a -DOPENSSL_SSL_LIBRARY=$OPENSSL_DIR/libssl.a -DCMAKE_TOOLCHAIN_FILE=$EMSDK_DIR/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CXX_FLAGS="-pthread -sUSE_ZLIB=1" .. + +cp -R ../crypto/smartcont ../crypto/fift/lib crypto + +emmake make -j4 funcfiftlib diff --git a/CMakeLists.txt b/CMakeLists.txt index 44dbae8b..936b74e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_EXTENSIONS FALSE) #BEGIN internal +option(USE_EMSCRIPTEN "Use \"ON\" for config building wasm." OFF) option(TON_ONLY_TONLIB "Use \"ON\" to build only tonlib." OFF) +if (USE_EMSCRIPTEN) + set(TON_ONLY_TONLIB true) +endif() if (TON_ONLY_TONLIB) set(NOT_TON_ONLY_TONLIB false) else() @@ -242,10 +246,14 @@ elseif (CLANG OR GCC) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -fvisibility=hidden -Wl,-dead_strip,-x,-S") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL") + if (NOT USE_EMSCRIPTEN) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL") + endif() set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") if (NOT TON_USE_ASAN AND NOT TON_USE_TSAN AND NOT MEMPROF) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--exclude-libs,ALL") + if (NOT USE_EMSCRIPTEN) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--exclude-libs,ALL") + endif() endif() endif() endif() diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 58b98088..c8c85370 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -179,6 +179,7 @@ set(FUNC_LIB_SOURCE func/stack-transform.cpp func/optimize.cpp func/codegen.cpp + func/func.cpp ) set(TLB_BLOCK_AUTO @@ -266,6 +267,8 @@ set(BIGINT_TEST_SOURCE PARENT_SCOPE ) +set(USE_EMSCRIPTEN ${USE_EMSCRIPTEN} PARENT_SCOPE) + add_library(ton_crypto STATIC ${TON_CRYPTO_SOURCE}) target_include_directories(ton_crypto PUBLIC $ @@ -305,13 +308,30 @@ target_include_directories(ton_block PUBLIC $ $) target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api) -add_executable(func func/func.cpp ${FUNC_LIB_SOURCE}) +add_executable(func func/func-main.cpp ${FUNC_LIB_SOURCE}) target_include_directories(func PUBLIC $) target_link_libraries(func PUBLIC ton_crypto src_parser git ton_block) if (WINGETOPT_FOUND) target_link_libraries_system(func wingetopt) endif() +if (USE_EMSCRIPTEN) + add_executable(funcfiftlib funcfiftlib/funcfiftlib.cpp ${FUNC_LIB_SOURCE}) + target_include_directories(funcfiftlib PUBLIC $) + target_link_libraries(funcfiftlib PUBLIC fift-lib src_parser git) + target_link_options(funcfiftlib PRIVATE -sEXPORTED_RUNTIME_METHODS=FS,ccall,cwrap,_malloc,free,UTF8ToString,stringToUTF8) + target_link_options(funcfiftlib PRIVATE -sEXPORTED_FUNCTIONS=_func_compile,_version) + target_link_options(funcfiftlib PRIVATE -sEXPORT_NAME=CompilerModule) + target_link_options(funcfiftlib PRIVATE -sERROR_ON_UNDEFINED_SYMBOLS=0) + target_link_options(funcfiftlib PRIVATE -sFILESYSTEM=1) + target_link_options(funcfiftlib PRIVATE -Oz) + target_link_options(funcfiftlib PRIVATE -sIGNORE_MISSING_MAIN=1) + target_link_options(funcfiftlib PRIVATE -sAUTO_NATIVE_LIBRARIES=0) + target_link_options(funcfiftlib PRIVATE -sMODULARIZE=1) + target_link_options(funcfiftlib PRIVATE --embed-file ${CMAKE_CURRENT_SOURCE_DIR}/fift/lib@/fiftlib) + target_compile_options(funcfiftlib PRIVATE -sDISABLE_EXCEPTION_CATCHING=0) +endif() + add_executable(tlbc tl/tlbc.cpp) target_include_directories(tlbc PUBLIC $) target_link_libraries(tlbc PUBLIC ton_crypto src_parser) @@ -337,16 +357,26 @@ if (TON_USE_ASAN AND NOT WIN32) endif() file(MAKE_DIRECTORY smartcont/auto) -if (NOT CMAKE_CROSSCOMPILING) +if (NOT CMAKE_CROSSCOMPILING OR USE_EMSCRIPTEN) set(GENERATE_TLB_CMD tlbc) - add_custom_command( - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block - COMMAND ${TURN_OFF_LSAN} - COMMAND ${GENERATE_TLB_CMD} -o block-auto -n block::gen -z block.tlb - COMMENT "Generate block tlb source files" - OUTPUT ${TLB_BLOCK_AUTO} - DEPENDS tlbc block/block.tlb - ) + if (NOT USE_EMSCRIPTEN) + add_custom_command( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block + COMMAND ${TURN_OFF_LSAN} + COMMAND ${GENERATE_TLB_CMD} -o block-auto -n block::gen -z block.tlb + COMMENT "Generate block tlb source files" + OUTPUT ${TLB_BLOCK_AUTO} + DEPENDS tlbc block/block.tlb + ) + else() + add_custom_command( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/block + COMMAND ${TURN_OFF_LSAN} + COMMENT "Generate block tlb source files" + OUTPUT ${TLB_BLOCK_AUTO} + DEPENDS tlbc block/block.tlb + ) + endif() add_custom_target(tlb_generate_block DEPENDS ${TLB_BLOCK_AUTO}) add_dependencies(ton_block tlb_generate_block) diff --git a/crypto/func/func-main.cpp b/crypto/func/func-main.cpp new file mode 100644 index 00000000..45194ea3 --- /dev/null +++ b/crypto/func/func-main.cpp @@ -0,0 +1,127 @@ +/* + 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 . + + In addition, as a special exception, the copyright holders give permission + to link the code of portions of this program with the OpenSSL library. + You must obey the GNU General Public License in all respects for all + of the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. If you delete this exception statement + from all source files in the program, then also delete it here. + + Copyright 2017-2020 Telegram Systems LLP +*/ +#include "func.h" +#include "parser/srcread.h" +#include "parser/lexer.h" +#include "parser/symtable.h" +#include +#include +#include "git.h" + +void usage(const char* progname) { + std::cerr + << "usage: " << progname + << " [-vIAPSR][-O][-i][-o][-W] { ...}\n" + "\tGenerates Fift TVM assembler code from a funC source\n" + "-I\tEnables interactive mode (parse stdin)\n" + "-o\tWrites generated code into specified file instead of stdout\n" + "-v\tIncreases verbosity level (extra information output into stderr)\n" + "-i\tSets indentation for the output code (in two-space units)\n" + "-A\tPrefix code with `\"Asm.fif\" include` preamble\n" + "-O\tSets optimization level (2 by default)\n" + "-P\tEnvelope code into PROGRAM{ ... }END>c\n" + "-S\tInclude stack layout comments in the output code\n" + "-R\tInclude operation rewrite comments in the output code\n" + "-W\tInclude Fift code to serialize and save generated code into specified BoC file. Enables " + "-A and -P.\n" + "\t-s\tOutput semantic version of FunC and exit\n" + "\t-V\tShow func build information\n"; + std::exit(2); +} + +int main(int argc, char* const argv[]) { + int i; + std::string output_filename; + while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) { + switch (i) { + case 'A': + funC::asm_preamble = true; + break; + case 'I': + funC::interactive = true; + break; + case 'i': + funC::indent = std::max(0, atoi(optarg)); + break; + case 'o': + output_filename = optarg; + break; + case 'O': + funC::opt_level = std::max(0, atoi(optarg)); + break; + case 'P': + funC::program_envelope = true; + break; + case 'R': + funC::op_rewrite_comments = true; + break; + case 'S': + funC::stack_layout_comments = true; + break; + case 'v': + ++funC::verbosity; + break; + case 'W': + funC::boc_output_filename = optarg; + funC::asm_preamble = funC::program_envelope = true; + break; + case 's': + std::cout << funC::func_version << "\n"; + std::exit(0); + break; + case 'V': + std::cout << "FunC semantic version: v" << funC::func_version << "\n"; + std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n"; + std::exit(0); + break; + case 'h': + default: + usage(argv[0]); + } + } + + std::ostream *outs = &std::cout; + + std::unique_ptr fs; + if (!output_filename.empty()) { + fs = std::make_unique(output_filename, fs->trunc | fs->out); + if (!fs->is_open()) { + std::cerr << "failed to create output file " << output_filename << '\n'; + return 2; + } + outs = fs.get(); + } + + std::vector sources; + + while (optind < argc) { + sources.push_back(std::string(argv[optind++])); + } + + return funC::func_proceed(sources, *outs, std::cerr); +} diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index b5d769cb..3daac5d7 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -28,16 +28,14 @@ #include "func.h" #include "parser/srcread.h" #include "parser/lexer.h" -#include "parser/symtable.h" #include -#include #include "git.h" namespace funC { int verbosity, indent, opt_level = 2; bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble; -std::ostream* outs = &std::cout; +bool interactive = false; std::string generated_from, boc_output_filename; /* @@ -46,58 +44,59 @@ std::string generated_from, boc_output_filename; * */ -void generate_output_func(SymDef* func_sym) { +void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &errs) { SymValCodeFunc* func_val = dynamic_cast(func_sym->value); assert(func_val); std::string name = sym::symbols.get_name(func_sym->sym_idx); if (verbosity >= 2) { - std::cerr << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl; + errs << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl; } if (!func_val->code) { - std::cerr << "( function `" << name << "` undefined )\n"; + errs << "( function `" << name << "` undefined )\n"; + throw src::ParseError(func_sym->loc, name); } else { CodeBlob& code = *(func_val->code); if (verbosity >= 3) { - code.print(std::cerr, 9); + code.print(errs, 9); } code.simplify_var_types(); if (verbosity >= 5) { - std::cerr << "after simplify_var_types: \n"; - code.print(std::cerr, 0); + errs << "after simplify_var_types: \n"; + code.print(errs, 0); } code.prune_unreachable_code(); if (verbosity >= 5) { - std::cerr << "after prune_unreachable: \n"; - code.print(std::cerr, 0); + errs << "after prune_unreachable: \n"; + code.print(errs, 0); } code.split_vars(true); if (verbosity >= 5) { - std::cerr << "after split_vars: \n"; - code.print(std::cerr, 0); + errs << "after split_vars: \n"; + code.print(errs, 0); } for (int i = 0; i < 8; i++) { code.compute_used_code_vars(); if (verbosity >= 4) { - std::cerr << "after compute_used_vars: \n"; - code.print(std::cerr, 6); + errs << "after compute_used_vars: \n"; + code.print(errs, 6); } code.fwd_analyze(); if (verbosity >= 5) { - std::cerr << "after fwd_analyze: \n"; - code.print(std::cerr, 6); + errs << "after fwd_analyze: \n"; + code.print(errs, 6); } code.prune_unreachable_code(); if (verbosity >= 5) { - std::cerr << "after prune_unreachable: \n"; - code.print(std::cerr, 6); + errs << "after prune_unreachable: \n"; + code.print(errs, 6); } } code.mark_noreturn(); if (verbosity >= 3) { - code.print(std::cerr, 15); + code.print(errs, 15); } if (verbosity >= 2) { - std::cerr << "\n---------- resulting code for " << name << " -------------\n"; + errs << "\n---------- resulting code for " << name << " -------------\n"; } bool inline_func = (func_val->flags & 1); bool inline_ref = (func_val->flags & 2); @@ -107,7 +106,7 @@ void generate_output_func(SymDef* func_sym) { } else if (inline_ref) { modifier = "REF"; } - *outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n"; + outs << std::string(indent * 2, ' ') << name << " PROC" << modifier << ":<{\n"; int mode = 0; if (stack_layout_comments) { mode |= Stack::_StkCmt | Stack::_CptStkCmt; @@ -120,145 +119,71 @@ void generate_output_func(SymDef* func_sym) { if (fv && (fv->flags & 1) && code.ops->noreturn()) { mode |= Stack::_InlineFunc; } - code.generate_code(*outs, mode, indent + 1); - *outs << std::string(indent * 2, ' ') << "}>\n"; + code.generate_code(outs, mode, indent + 1); + outs << std::string(indent * 2, ' ') << "}>\n"; if (verbosity >= 2) { - std::cerr << "--------------\n"; + errs << "--------------\n"; } } } -int generate_output() { +int generate_output(std::ostream &outs, std::ostream &errs) { if (asm_preamble) { - *outs << "\"Asm.fif\" include\n"; + outs << "\"Asm.fif\" include\n"; } - *outs << "// automatically generated from " << generated_from << std::endl; + outs << "// automatically generated from " << generated_from << std::endl; if (program_envelope) { - *outs << "PROGRAM{\n"; + outs << "PROGRAM{\n"; } for (SymDef* func_sym : glob_func) { SymValCodeFunc* func_val = dynamic_cast(func_sym->value); assert(func_val); std::string name = sym::symbols.get_name(func_sym->sym_idx); - *outs << std::string(indent * 2, ' '); + outs << std::string(indent * 2, ' '); if (func_val->method_id.is_null()) { - *outs << "DECLPROC " << name << "\n"; + outs << "DECLPROC " << name << "\n"; } else { - *outs << func_val->method_id << " DECLMETHOD " << name << "\n"; + outs << func_val->method_id << " DECLMETHOD " << name << "\n"; } } for (SymDef* gvar_sym : glob_vars) { assert(dynamic_cast(gvar_sym->value)); std::string name = sym::symbols.get_name(gvar_sym->sym_idx); - *outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n"; + outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n"; } int errors = 0; for (SymDef* func_sym : glob_func) { try { - generate_output_func(func_sym); + generate_output_func(func_sym, outs, errs); } catch (src::Error& err) { - std::cerr << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n" + errs << "cannot generate code for function `" << sym::symbols.get_name(func_sym->sym_idx) << "`:\n" << err << std::endl; ++errors; } } if (program_envelope) { - *outs << "}END>c\n"; + outs << "}END>c\n"; } if (!boc_output_filename.empty()) { - *outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n"; + outs << "2 boc+>B \"" << boc_output_filename << "\" B>file\n"; } return errors; } -} // namespace funC - -void usage(const char* progname) { - std::cerr - << "usage: " << progname - << " [-vIAPSR][-O][-i][-o][-W] { ...}\n" - "\tGenerates Fift TVM assembler code from a funC source\n" - "-I\tEnables interactive mode (parse stdin)\n" - "-o\tWrites generated code into specified file instead of stdout\n" - "-v\tIncreases verbosity level (extra information output into stderr)\n" - "-i\tSets indentation for the output code (in two-space units)\n" - "-A\tPrefix code with `\"Asm.fif\" include` preamble\n" - "-O\tSets optimization level (2 by default)\n" - "-P\tEnvelope code into PROGRAM{ ... }END>c\n" - "-S\tInclude stack layout comments in the output code\n" - "-R\tInclude operation rewrite comments in the output code\n" - "-W\tInclude Fift code to serialize and save generated code into specified BoC file. Enables " - "-A and -P.\n" - "\t-s\tOutput semantic version of FunC and exit\n" - "\t-V\tShow func build information\n"; - std::exit(2); -} - -void output_inclusion_stack() { +void output_inclusion_stack(std::ostream &errs) { while (!funC::inclusion_locations.empty()) { src::SrcLocation loc = funC::inclusion_locations.top(); funC::inclusion_locations.pop(); if (loc.fdescr) { - std::cerr << "note: included from "; - loc.show(std::cerr); - std::cerr << std::endl; + errs << "note: included from "; + loc.show(errs); + errs << std::endl; } } } -std::string output_filename; - -int main(int argc, char* const argv[]) { - int i; - bool interactive = false; - while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) { - switch (i) { - case 'A': - funC::asm_preamble = true; - break; - case 'I': - interactive = true; - break; - case 'i': - funC::indent = std::max(0, atoi(optarg)); - break; - case 'o': - output_filename = optarg; - break; - case 'O': - funC::opt_level = std::max(0, atoi(optarg)); - break; - case 'P': - funC::program_envelope = true; - break; - case 'R': - funC::op_rewrite_comments = true; - break; - case 'S': - funC::stack_layout_comments = true; - break; - case 'v': - ++funC::verbosity; - break; - case 'W': - funC::boc_output_filename = optarg; - funC::asm_preamble = funC::program_envelope = true; - break; - case 's': - std::cout << funC::func_version << "\n"; - std::exit(0); - break; - case 'V': - std::cout << "FunC semantic version: v" << funC::func_version << "\n"; - std::cout << "Build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n"; - std::exit(0); - break; - case 'h': - default: - usage(argv[0]); - } - } +int func_proceed(const std::vector &sources, std::ostream &outs, std::ostream &errs) { if (funC::program_envelope && !funC::indent) { funC::indent = 1; } @@ -268,12 +193,11 @@ int main(int argc, char* const argv[]) { int ok = 0, proc = 0; try { - while (optind < argc) { - // funC::generated_from += std::string{"`"} + argv[optind] + "` "; - ok += funC::parse_source_file(argv[optind++]); + for (auto src : sources) { + ok += funC::parse_source_file(src.c_str()); proc++; } - if (interactive) { + if (funC::interactive) { funC::generated_from += "stdin "; ok += funC::parse_source_stdin(); proc++; @@ -284,29 +208,24 @@ int main(int argc, char* const argv[]) { if (!proc) { throw src::Fatal{"no source files, no output"}; } - std::unique_ptr fs; - if (!output_filename.empty()) { - fs = std::make_unique(output_filename, fs->trunc | fs->out); - if (!fs->is_open()) { - std::cerr << "failed to create output file " << output_filename << '\n'; - return 2; - } - funC::outs = fs.get(); - } - funC::generate_output(); + return funC::generate_output(outs, errs); } catch (src::Fatal& fatal) { - std::cerr << "fatal: " << fatal << std::endl; - output_inclusion_stack(); - std::exit(1); + errs << "fatal: " << fatal << std::endl; + output_inclusion_stack(errs); + return 2; } catch (src::Error& error) { - std::cerr << error << std::endl; - output_inclusion_stack(); - std::exit(1); + errs << error << std::endl; + output_inclusion_stack(errs); + return 2; } catch (funC::UnifyError& unif_err) { - std::cerr << "fatal: "; - unif_err.print_message(std::cerr); - std::cerr << std::endl; - output_inclusion_stack(); - std::exit(1); + errs << "fatal: "; + unif_err.print_message(errs); + errs << std::endl; + output_inclusion_stack(errs); + return 2; } + + return 0; } + +} // namespace funC \ No newline at end of file diff --git a/crypto/func/func.h b/crypto/func/func.h index 886a21ed..2ea1015e 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -1665,4 +1665,19 @@ AsmOp push_const(td::RefInt256 x); void define_builtins(); + +extern int verbosity, indent, opt_level; +extern bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble, interactive; +extern std::string generated_from, boc_output_filename; + +/* + * + * OUTPUT CODE GENERATOR + * + */ + +int func_proceed(const std::vector &sources, std::ostream &outs, std::ostream &errs); + } // namespace funC + + diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index 0d2aa985..439dfb88 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -1657,7 +1657,14 @@ bool parse_source_file(const char* filename, src::Lexem lex) { throw src::Fatal{msg}; } } - std::string real_filename = td::realpath(td::CSlice(filename)).move_as_ok(); + + auto path_res = td::realpath(td::CSlice(filename)); + if (path_res.is_error()) { + auto error = path_res.move_as_error(); + lex.error(error.message().c_str()); + return false; + } + std::string real_filename = path_res.move_as_ok(); if (std::count(source_files.begin(), source_files.end(), real_filename)) { if (verbosity >= 2) { if (lex.tp) { diff --git a/crypto/funcfiftlib/funcfiftlib.cpp b/crypto/funcfiftlib/funcfiftlib.cpp new file mode 100644 index 00000000..6c8912bc --- /dev/null +++ b/crypto/funcfiftlib/funcfiftlib.cpp @@ -0,0 +1,131 @@ +/* + 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 . + + In addition, as a special exception, the copyright holders give permission + to link the code of portions of this program with the OpenSSL library. + You must obey the GNU General Public License in all respects for all + of the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. If you delete this exception statement + from all source files in the program, then also delete it here. + + Copyright 2017-2020 Telegram Systems LLP +*/ +#include "func/func.h" +#include "git.h" +#include "td/utils/JsonBuilder.h" +#include "fift/utils.h" +#include "td/utils/base64.h" +#include +#include + +std::string escape_json(const std::string &s) { + std::ostringstream o; + for (auto c = s.cbegin(); c != s.cend(); c++) { + switch (*c) { + case '"': o << "\\\""; break; + case '\\': o << "\\\\"; break; + case '\b': o << "\\b"; break; + case '\f': o << "\\f"; break; + case '\n': o << "\\n"; break; + case '\r': o << "\\r"; break; + case '\t': o << "\\t"; break; + default: + if ('\x00' <= *c && *c <= '\x1f') { + o << "\\u" + << std::hex << std::setw(4) << std::setfill('0') << static_cast(*c); + } else { + o << *c; + } + } + } + return o.str(); +} + +td::Result compile_internal(char *config_json) { + TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json))) + auto &obj = input_json.get_object(); + + TRY_RESULT(opt_level, td::get_json_object_int_field(obj, "optLevel", false)); + TRY_RESULT(sources_obj, td::get_json_object_field(obj, "sources", td::JsonValue::Type::Array, false)); + + auto &sources_arr = sources_obj.get_array(); + + std::vector sources; + + for (auto &item : sources_arr) { + sources.push_back(item.get_string().str()); + } + + funC::opt_level = std::max(0, opt_level); + funC::program_envelope = true; + funC::verbosity = 0; + funC::indent = 1; + + std::ostringstream outs, errs; + auto compile_res = funC::func_proceed(sources, outs, errs); + + if (compile_res != 0) { + return td::Status::Error(std::string("Func compilation error: ") + errs.str()); + } + + TRY_RESULT(code_cell, fift::compile_asm(outs.str(), "/fiftlib/", false)); + TRY_RESULT(boc, vm::std_boc_serialize(code_cell)); + + td::JsonBuilder result_json; + auto result_obj = result_json.enter_object(); + result_obj("status", "ok"); + result_obj("codeBoc", td::base64_encode(boc)); + result_obj("fiftCode", escape_json(outs.str())); + result_obj.leave(); + + outs.clear(); + errs.clear(); + + return result_json.string_builder().as_cslice().str(); +} + +extern "C" { + +const char* version() { + auto version_json = td::JsonBuilder(); + auto obj = version_json.enter_object(); + obj("funcVersion", funC::func_version); + obj("funcFiftLibCommitHash", GitMetadata::CommitSHA1()); + obj("funcFiftLibCommitDate", GitMetadata::CommitDate()); + obj.leave(); + return strdup(version_json.string_builder().as_cslice().c_str()); +} + +const char *func_compile(char *config_json) { + auto res = compile_internal(config_json); + + if (res.is_error()) { + auto result = res.move_as_error(); + auto error_res = td::JsonBuilder(); + auto error_o = error_res.enter_object(); + error_o("status", "error"); + error_o("message", result.message().str()); + error_o.leave(); + return strdup(error_res.string_builder().as_cslice().c_str()); + } + + auto res_string = res.move_as_ok(); + + return strdup(res_string.c_str()); +} +} diff --git a/crypto/vm/large-boc-serializer.cpp b/crypto/vm/large-boc-serializer.cpp index 765bd9a6..fe16b767 100644 --- a/crypto/vm/large-boc-serializer.cpp +++ b/crypto/vm/large-boc-serializer.cpp @@ -313,7 +313,7 @@ td::Status LargeBocSerializer::serialize(td::FileFd& fd, int mode) { return td::Status::Error("bag of cells is too large"); } - boc_writers::FileWriter writer{fd, info.total_size}; + boc_writers::FileWriter writer{fd, (size_t) info.total_size}; auto store_ref = [&](unsigned long long value) { writer.store_uint(value, info.ref_byte_size); }; diff --git a/tdutils/td/utils/port/config.h b/tdutils/td/utils/port/config.h index 2bd671b0..77143668 100644 --- a/tdutils/td/utils/port/config.h +++ b/tdutils/td/utils/port/config.h @@ -39,7 +39,7 @@ #define TD_EVENTFD_BSD 1 #elif TD_EMSCRIPTEN #define TD_POLL_POLL 1 - #define TD_EVENTFD_UNSUPPORTED 1 + // #define TD_EVENTFD_UNSUPPORTED 1 #elif TD_DARWIN #define TD_POLL_KQUEUE 1 #define TD_EVENTFD_BSD 1 @@ -51,7 +51,11 @@ #endif #if TD_EMSCRIPTEN - #define TD_THREAD_UNSUPPORTED 1 + // #define TD_THREAD_UNSUPPORTED 1 + #define TD_POLL_EPOLL 1 + #define TD_EVENTFD_UNSUPPORTED 0 + #define TD_THREAD_PTHREAD 1 + #define TD_EVENTFD_LINUX 1 #elif TD_TIZEN || TD_LINUX || TD_DARWIN #define TD_THREAD_PTHREAD 1 #else diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index 5b6530a6..061778af 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -49,6 +49,8 @@ set(TONLIB_SOURCE set(TONLIB_OFFLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/offline.cpp PARENT_SCOPE) set(TONLIB_ONLINE_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/online.cpp PARENT_SCOPE) +set(USE_EMSCRIPTEN ${USE_EMSCRIPTEN} PARENT_SCOPE) + add_library(tonlib STATIC ${TONLIB_SOURCE}) target_include_directories(tonlib PUBLIC @@ -87,8 +89,12 @@ set(TONLIB_JSON_HEADERS tonlib/tonlib_client_json.h) set(TONLIB_JSON_SOURCE tonlib/tonlib_client_json.cpp) include(GenerateExportHeader) +if (NOT USE_EMSCRIPTEN) + add_library(tonlibjson SHARED ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS}) +else() + add_library(tonlibjson STATIC ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS}) +endif() -add_library(tonlibjson SHARED ${TONLIB_JSON_SOURCE} ${TONLIB_JSON_HEADERS}) target_link_libraries(tonlibjson PRIVATE tonlibjson_private) generate_export_header(tonlibjson EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h) target_include_directories(tonlibjson PUBLIC @@ -149,11 +155,13 @@ endif() install(FILES ${TONLIB_JSON_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/tonlib/tonlibjson_export.h DESTINATION include/tonlib/) -install(EXPORT Tonlib - FILE TonlibTargets.cmake - NAMESPACE Tonlib:: - DESTINATION lib/cmake/Tonlib -) +if (NOT USE_EMSCRIPTEN) + install(EXPORT Tonlib + FILE TonlibTargets.cmake + NAMESPACE Tonlib:: + DESTINATION lib/cmake/Tonlib + ) +endif() include(CMakePackageConfigHelpers) write_basic_package_version_file("TonlibConfigVersion.cmake" VERSION ${TON_VERSION}