mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
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 <tsivarev.a@gmail.com> Co-authored-by: EmelyanenkoK <emelyanenko.kirill@gmail.com> Co-authored-by: tolya-yanot <1449561+tolya-yanot@users.noreply.github.com>
This commit is contained in:
parent
45e99a5c63
commit
e2cca03a78
11 changed files with 491 additions and 163 deletions
79
.github/script/fift-func-wasm-build-ubuntu.sh
vendored
Executable file
79
.github/script/fift-func-wasm-build-ubuntu.sh
vendored
Executable file
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
|
@ -305,13 +308,30 @@ target_include_directories(ton_block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SO
|
|||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
||||
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)
|
||||
|
||||
|
|
127
crypto/func/func-main.cpp
Normal file
127
crypto/func/func-main.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <getopt.h>
|
||||
#include <fstream>
|
||||
#include "git.h"
|
||||
|
||||
void usage(const char* progname) {
|
||||
std::cerr
|
||||
<< "usage: " << progname
|
||||
<< " [-vIAPSR][-O<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
|
||||
"\tGenerates Fift TVM assembler code from a funC source\n"
|
||||
"-I\tEnables interactive mode (parse stdin)\n"
|
||||
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
|
||||
"-v\tIncreases verbosity level (extra information output into stderr)\n"
|
||||
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
|
||||
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
|
||||
"-O<level>\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<output-boc-file>\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<version>\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<std::fstream> fs;
|
||||
if (!output_filename.empty()) {
|
||||
fs = std::make_unique<std::fstream>(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<std::string> sources;
|
||||
|
||||
while (optind < argc) {
|
||||
sources.push_back(std::string(argv[optind++]));
|
||||
}
|
||||
|
||||
return funC::func_proceed(sources, *outs, std::cerr);
|
||||
}
|
|
@ -28,16 +28,14 @@
|
|||
#include "func.h"
|
||||
#include "parser/srcread.h"
|
||||
#include "parser/lexer.h"
|
||||
#include "parser/symtable.h"
|
||||
#include <getopt.h>
|
||||
#include <fstream>
|
||||
#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<SymValCodeFunc*>(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<SymValCodeFunc*>(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<SymValGlobVar*>(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<level>][-i<indent-spc>][-o<output-filename>][-W<boc-filename>] {<func-source-filename> ...}\n"
|
||||
"\tGenerates Fift TVM assembler code from a funC source\n"
|
||||
"-I\tEnables interactive mode (parse stdin)\n"
|
||||
"-o<fift-output-filename>\tWrites generated code into specified file instead of stdout\n"
|
||||
"-v\tIncreases verbosity level (extra information output into stderr)\n"
|
||||
"-i<indent>\tSets indentation for the output code (in two-space units)\n"
|
||||
"-A\tPrefix code with `\"Asm.fif\" include` preamble\n"
|
||||
"-O<level>\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<output-boc-file>\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<version>\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<std::string> &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<std::fstream> fs;
|
||||
if (!output_filename.empty()) {
|
||||
fs = std::make_unique<std::fstream>(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
|
|
@ -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<std::string> &sources, std::ostream &outs, std::ostream &errs);
|
||||
|
||||
} // namespace funC
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
131
crypto/funcfiftlib/funcfiftlib.cpp
Normal file
131
crypto/funcfiftlib/funcfiftlib.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
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<int>(*c);
|
||||
} else {
|
||||
o << *c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return o.str();
|
||||
}
|
||||
|
||||
td::Result<std::string> 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<std::string> 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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue