From 617a114a2c4d7cdb766bd111375237fd9cb15663 Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 14:21:10 +0100 Subject: cmake: make 3.0 minimum version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Probably using 3.0+ features anyway and should silence following warning: CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required): Compatibility with CMake < 2.8.12 will be removed from a future version of CMake. Update the VERSION argument value or use a ... suffix to tell CMake that the project does not need compatibility with older versions. Signed-off-by: Petr Štetiar --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2046392..e3dfe81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.0) include(CheckFunctionExists) include(CheckSymbolExists) -- cgit v1.2.3 From 3c2aeffa26ba720f130d2acff34139fa774cec7e Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 13:42:25 +0100 Subject: cmake: fix includes and libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So it can be built out of the tree. Signed-off-by: Petr Štetiar --- CMakeLists.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3dfe81..3a8fce0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ IF(NOT APPLE) SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,--gc-sections") ENDIF() -find_library(json NAMES json-c json) +FIND_LIBRARY(json NAMES json-c json) IF(DEBUG) ADD_DEFINITIONS(-DDEBUG -g3 -O0) @@ -71,17 +71,24 @@ IF(MATH_SUPPORT) ENDIF() IF(UBUS_SUPPORT) + FIND_LIBRARY(ubus NAMES ubus) + FIND_LIBRARY(blobmsg_json NAMES blobmsg_json) + FIND_PATH(ubus_include_dir NAMES libubus.h) + INCLUDE_DIRECTORIES(${ubus_include_dir}) SET(LIBRARIES ${LIBRARIES} ubus_lib) ADD_LIBRARY(ubus_lib MODULE lib/ubus.c) SET_TARGET_PROPERTIES(ubus_lib PROPERTIES OUTPUT_NAME lib/ubus PREFIX "") - TARGET_LINK_LIBRARIES(ubus_lib ubus blobmsg_json) + TARGET_LINK_LIBRARIES(ubus_lib ${ubus} ${blobmsg_json}) ENDIF() IF(UCI_SUPPORT) + FIND_LIBRARY(uci NAMES uci) + FIND_PATH(uci_include_dir uci.h) + INCLUDE_DIRECTORIES(${uci_include_dir}) SET(LIBRARIES ${LIBRARIES} uci_lib) ADD_LIBRARY(uci_lib MODULE lib/uci.c) SET_TARGET_PROPERTIES(uci_lib PROPERTIES OUTPUT_NAME lib/uci PREFIX "") - TARGET_LINK_LIBRARIES(uci_lib uci) + TARGET_LINK_LIBRARIES(uci_lib ${uci}) ENDIF() INSTALL(TARGETS ucode RUNTIME DESTINATION bin) -- cgit v1.2.3 From 502ecdcb7e6efc669ed7d7dc62a9044c7e05a530 Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 13:41:32 +0100 Subject: cmake: enable extra compiler checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's enforce additional automatic checks enforced by the compiler in order to catch possible errors during compilation. Signed-off-by: Petr Štetiar --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a8fce0..53de0b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,13 @@ include(CheckFunctionExists) include(CheckSymbolExists) PROJECT(ucode C) -ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -Wmissing-declarations -Wno-error=unused-variable -ffunction-sections -D_GNU_SOURCE) +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -ffunction-sections -D_GNU_SOURCE) + +IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6) + ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration) + ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral) +ENDIF() +ADD_DEFINITIONS(-Wmissing-declarations -Wno-error=unused-variable -Wno-unused-parameter) OPTION(FS_SUPPORT "Filesystem plugin support" ON) OPTION(MATH_SUPPORT "Math plugin support" ON) -- cgit v1.2.3 From 778e4f7bf205d04d631f968e5b33cfdf9e8121e1 Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 14:31:17 +0100 Subject: lexer: fix incomplete struct initializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes bunch of following warnings: lexer.c:68:37: warning: missing field 'parse' initializer [-Wmissing-field-initializers] lexer.c:138:34: warning: missing field '' initializer [-Wmissing-field-initializers] Signed-off-by: Petr Štetiar --- lexer.c | 166 ++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/lexer.c b/lexer.c index bf099f5..ada8671 100644 --- a/lexer.c +++ b/lexer.c @@ -65,103 +65,103 @@ static uc_token *parse_number(uc_lexer *, bool); static uc_token *parse_label(uc_lexer *, bool); static const struct token tokens[] = { - { TK_ASLEFT, { .pat = "<<=" }, 3 }, - { TK_ASRIGHT, { .pat = ">>=" }, 3 }, - { TK_LEXP, { .pat = "{{-" }, 3 }, - { TK_REXP, { .pat = "-}}" }, 3 }, - { TK_LSTM, { .pat = "{%+" }, 3 }, - { TK_LSTM, { .pat = "{%-" }, 3 }, - { TK_RSTM, { .pat = "-%}" }, 3 }, - { TK_EQS, { .pat = "===" }, 3 }, - { TK_NES, { .pat = "!==" }, 3 }, - { TK_ELLIP, { .pat = "..." }, 3 }, - { TK_AND, { .pat = "&&" }, 2 }, - { TK_ASADD, { .pat = "+=" }, 2 }, - { TK_ASBAND, { .pat = "&=" }, 2 }, - { TK_ASBOR, { .pat = "|=" }, 2 }, - { TK_ASBXOR, { .pat = "^=" }, 2 }, - //{ TK_ASDIV, { .pat = "/=" }, 2 }, - { TK_ASMOD, { .pat = "%=" }, 2 }, - { TK_ASMUL, { .pat = "*=" }, 2 }, - { TK_ASSUB, { .pat = "-=" }, 2 }, - { TK_DEC, { .pat = "--" }, 2 }, - { TK_INC, { .pat = "++" }, 2 }, - { TK_EQ, { .pat = "==" }, 2 }, - { TK_NE, { .pat = "!=" }, 2 }, - { TK_LE, { .pat = "<=" }, 2 }, - { TK_GE, { .pat = ">=" }, 2 }, - { TK_LSHIFT, { .pat = "<<" }, 2 }, - { TK_RSHIFT, { .pat = ">>" }, 2 }, + { TK_ASLEFT, { .pat = "<<=" }, 3, NULL }, + { TK_ASRIGHT, { .pat = ">>=" }, 3, NULL }, + { TK_LEXP, { .pat = "{{-" }, 3, NULL }, + { TK_REXP, { .pat = "-}}" }, 3, NULL }, + { TK_LSTM, { .pat = "{%+" }, 3, NULL }, + { TK_LSTM, { .pat = "{%-" }, 3, NULL }, + { TK_RSTM, { .pat = "-%}" }, 3, NULL }, + { TK_EQS, { .pat = "===" }, 3, NULL }, + { TK_NES, { .pat = "!==" }, 3, NULL }, + { TK_ELLIP, { .pat = "..." }, 3, NULL }, + { TK_AND, { .pat = "&&" }, 2, NULL }, + { TK_ASADD, { .pat = "+=" }, 2, NULL }, + { TK_ASBAND, { .pat = "&=" }, 2, NULL }, + { TK_ASBOR, { .pat = "|=" }, 2, NULL }, + { TK_ASBXOR, { .pat = "^=" }, 2, NULL }, + //{ TK_ASDIV, { .pat = "/=" }, 2, NULL }, + { TK_ASMOD, { .pat = "%=" }, 2, NULL }, + { TK_ASMUL, { .pat = "*=" }, 2, NULL }, + { TK_ASSUB, { .pat = "-=" }, 2, NULL }, + { TK_DEC, { .pat = "--" }, 2, NULL }, + { TK_INC, { .pat = "++" }, 2, NULL }, + { TK_EQ, { .pat = "==" }, 2, NULL }, + { TK_NE, { .pat = "!=" }, 2, NULL }, + { TK_LE, { .pat = "<=" }, 2, NULL }, + { TK_GE, { .pat = ">=" }, 2, NULL }, + { TK_LSHIFT, { .pat = "<<" }, 2, NULL }, + { TK_RSHIFT, { .pat = ">>" }, 2, NULL }, { 0, { .pat = "//" }, 2, parse_comment }, { 0, { .pat = "/*" }, 2, parse_comment }, - { TK_OR, { .pat = "||" }, 2 }, - { TK_LEXP, { .pat = "{{" }, 2 }, - { TK_REXP, { .pat = "}}" }, 2 }, - { TK_LSTM, { .pat = "{%" }, 2 }, - { TK_RSTM, { .pat = "%}" }, 2 }, - { TK_ARROW, { .pat = "=>" }, 2 }, - { TK_ADD, { .pat = "+" }, 1 }, - { TK_ASSIGN, { .pat = "=" }, 1 }, - { TK_BAND, { .pat = "&" }, 1 }, - { TK_BOR, { .pat = "|" }, 1 }, - { TK_LBRACK, { .pat = "[" }, 1 }, - { TK_RBRACK, { .pat = "]" }, 1 }, - { TK_BXOR, { .pat = "^" }, 1 }, - { TK_LBRACE, { .pat = "{" }, 1 }, - { TK_RBRACE, { .pat = "}" }, 1 }, - { TK_COLON, { .pat = ":" }, 1 }, - { TK_COMMA, { .pat = "," }, 1 }, - { TK_COMPL, { .pat = "~" }, 1 }, - //{ TK_DIV, { .pat = "/" }, 1 }, - { TK_GT, { .pat = ">" }, 1 }, - { TK_NOT, { .pat = "!" }, 1 }, - { TK_LT, { .pat = "<" }, 1 }, - { TK_MOD, { .pat = "%" }, 1 }, - { TK_MUL, { .pat = "*" }, 1 }, - { TK_LPAREN, { .pat = "(" }, 1 }, - { TK_RPAREN, { .pat = ")" }, 1 }, - { TK_QMARK, { .pat = "?" }, 1 }, - { TK_SCOL, { .pat = ";" }, 1 }, - //{ TK_SUB, { .pat = "-" }, 1 }, - { TK_DOT, { .pat = "." }, 1 }, + { TK_OR, { .pat = "||" }, 2, NULL }, + { TK_LEXP, { .pat = "{{" }, 2, NULL }, + { TK_REXP, { .pat = "}}" }, 2, NULL }, + { TK_LSTM, { .pat = "{%" }, 2, NULL }, + { TK_RSTM, { .pat = "%}" }, 2, NULL }, + { TK_ARROW, { .pat = "=>" }, 2, NULL }, + { TK_ADD, { .pat = "+" }, 1, NULL }, + { TK_ASSIGN, { .pat = "=" }, 1, NULL }, + { TK_BAND, { .pat = "&" }, 1, NULL }, + { TK_BOR, { .pat = "|" }, 1, NULL }, + { TK_LBRACK, { .pat = "[" }, 1, NULL }, + { TK_RBRACK, { .pat = "]" }, 1, NULL }, + { TK_BXOR, { .pat = "^" }, 1, NULL }, + { TK_LBRACE, { .pat = "{" }, 1, NULL }, + { TK_RBRACE, { .pat = "}" }, 1, NULL }, + { TK_COLON, { .pat = ":" }, 1, NULL }, + { TK_COMMA, { .pat = "," }, 1, NULL }, + { TK_COMPL, { .pat = "~" }, 1, NULL }, + //{ TK_DIV, { .pat = "/" }, 1, NULL }, + { TK_GT, { .pat = ">" }, 1, NULL }, + { TK_NOT, { .pat = "!" }, 1, NULL }, + { TK_LT, { .pat = "<" }, 1, NULL }, + { TK_MOD, { .pat = "%" }, 1, NULL }, + { TK_MUL, { .pat = "*" }, 1, NULL }, + { TK_LPAREN, { .pat = "(" }, 1, NULL }, + { TK_RPAREN, { .pat = ")" }, 1, NULL }, + { TK_QMARK, { .pat = "?" }, 1, NULL }, + { TK_SCOL, { .pat = ";" }, 1, NULL }, + //{ TK_SUB, { .pat = "-" }, 1, NULL }, + { TK_DOT, { .pat = "." }, 1, NULL }, { TK_STRING, { .pat = "'" }, 1, parse_string }, { TK_STRING, { .pat = "\"" }, 1, parse_string }, { TK_REGEXP, { .pat = "/" }, 1, parse_regexp }, - { TK_LABEL, { .pat = "_" }, 1, parse_label }, - { TK_LABEL, { .pat = "az" }, 0, parse_label }, - { TK_LABEL, { .pat = "AZ" }, 0, parse_label }, + { TK_LABEL, { .pat = "_" }, 1, parse_label }, + { TK_LABEL, { .pat = "az" }, 0, parse_label }, + { TK_LABEL, { .pat = "AZ" }, 0, parse_label }, { TK_NUMBER, { .pat = "-" }, 1, parse_number }, { TK_NUMBER, { .pat = "09" }, 0, parse_number }, }; static const struct keyword reserved_words[] = { - { TK_ENDFUNC, "endfunction", 11 }, + { TK_ENDFUNC, "endfunction", 11, {} }, { TK_DOUBLE, "Infinity", 8, { .d = INFINITY } }, - { TK_CONTINUE, "continue", 8 }, - { TK_ENDWHILE, "endwhile", 8 }, - { TK_FUNC, "function", 8 }, - { TK_DEFAULT, "default", 7 }, - { TK_RETURN, "return", 6 }, - { TK_ENDFOR, "endfor", 6 }, - { TK_SWITCH, "switch", 6 }, - { TK_LOCAL, "local", 5 }, - { TK_ENDIF, "endif", 5 }, - { TK_WHILE, "while", 5 }, - { TK_BREAK, "break", 5 }, - { TK_CATCH, "catch", 5 }, + { TK_CONTINUE, "continue", 8, {} }, + { TK_ENDWHILE, "endwhile", 8, {} }, + { TK_FUNC, "function", 8, {} }, + { TK_DEFAULT, "default", 7, {} }, + { TK_RETURN, "return", 6, {} }, + { TK_ENDFOR, "endfor", 6, {} }, + { TK_SWITCH, "switch", 6, {} }, + { TK_LOCAL, "local", 5, {} }, + { TK_ENDIF, "endif", 5, {} }, + { TK_WHILE, "while", 5, {} }, + { TK_BREAK, "break", 5, {} }, + { TK_CATCH, "catch", 5, {} }, { TK_BOOL, "false", 5, { .b = false } }, { TK_BOOL, "true", 4, { .b = true } }, - { TK_ELIF, "elif", 4 }, - { TK_ELSE, "else", 4 }, - { TK_THIS, "this", 4 }, - { TK_NULL, "null", 4 }, - { TK_CASE, "case", 4 }, + { TK_ELIF, "elif", 4, {} }, + { TK_ELSE, "else", 4, {} }, + { TK_THIS, "this", 4, {} }, + { TK_NULL, "null", 4, {} }, + { TK_CASE, "case", 4, {} }, { TK_DOUBLE, "NaN", 3, { .d = NAN } }, - { TK_TRY, "try", 3 }, - { TK_FOR, "for", 3 }, - { TK_LOCAL, "let", 3 }, - { TK_IF, "if", 2 }, - { TK_IN, "in", 2 }, + { TK_TRY, "try", 3, {} }, + { TK_FOR, "for", 3, {} }, + { TK_LOCAL, "let", 3, {} }, + { TK_IF, "if", 2, {} }, + { TK_IN, "in", 2, {} }, }; -- cgit v1.2.3 From 80393611fb6634abcc0da1dee2da7c4418dbde8d Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 17:06:09 +0100 Subject: main: provide just binary name in help output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise it prints out complete path which is probably not desired and we would need to filter out paths in the test's output etc. Signed-off-by: Petr Štetiar --- main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 36d42a8..55c9dd8 100644 --- a/main.c +++ b/main.c @@ -36,7 +36,7 @@ static void -print_usage(char *app) +print_usage(const char *app) { printf( "== Usage ==\n\n" @@ -51,7 +51,7 @@ print_usage(char *app) " -e Set global variables from given JSON object\n" " -E Set global variables from given JSON file\n" " -m Preload given module\n", - app); + basename(app)); } static void -- cgit v1.2.3 From 2b59097c3f61fa901e91ac4cea48940760439578 Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 16:54:55 +0100 Subject: tests: create custom tests from current tests cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Petr Štetiar --- run_tests.sh | 178 ----------- tests/00_syntax/00_single_line_comments | 15 - tests/00_syntax/01_unterminated_comment | 15 - tests/00_syntax/02_multi_line_comments | 12 - tests/00_syntax/03_expression_blocks | 11 - tests/00_syntax/04_statement_blocks | 20 -- tests/00_syntax/05_block_nesting | 24 -- tests/00_syntax/06_open_statement_block | 13 - tests/00_syntax/07_embedded_single_line_comments | 21 -- tests/00_syntax/08_embedded_multi_line_comments | 24 -- tests/00_syntax/09_string_literals | 50 ---- tests/00_syntax/10_numeric_literals | 26 -- tests/00_syntax/11_misc_literals | 17 -- tests/00_syntax/12_block_whitespace_control | 47 --- tests/00_syntax/13_object_literals | 174 ----------- tests/00_syntax/14_array_literals | 82 ------ tests/00_syntax/15_function_declarations | 164 ----------- tests/00_syntax/16_for_loop | 299 ------------------- tests/00_syntax/17_while_loop | 71 ----- tests/00_syntax/18_if_condition | 121 -------- tests/00_syntax/19_arrow_functions | 124 -------- tests/00_syntax/20_list_expressions | 45 --- tests/00_syntax/21_regex_literals | 89 ------ tests/01_arithmetic/00_value_conversion | 125 -------- tests/01_arithmetic/01_division | 53 ---- tests/01_arithmetic/02_modulo | 32 -- tests/01_arithmetic/03_bitwise | 54 ---- tests/01_arithmetic/04_inc_dec | 49 ---- tests/02_runtime/00_scoping | 161 ---------- tests/02_runtime/01_break_continue | 50 ---- tests/02_runtime/02_this | 50 ---- tests/02_runtime/03_try_catch | 138 --------- tests/02_runtime/04_switch_case | 325 --------------------- tests/02_runtime/05_closure_scope | 35 --- tests/02_runtime/06_recursion | 59 ---- tests/03_bugs/01_try_catch_stack_mismatch | 52 ---- tests/03_bugs/02_array_pop_use_after_free | 14 - tests/03_bugs/03_switch_fallthrough_miscompilation | 16 - tests/03_bugs/04_property_set_abort | 76 ----- tests/03_bugs/05_duplicate_ressource_type | 31 -- tests/03_bugs/06_lexer_escape_at_boundary | 12 - tests/03_bugs/07_lexer_overlong_lines | 13 - tests/03_bugs/08_compiler_arrow_fn_expressions | 15 - tests/03_bugs/09_reject_invalid_array_indexes | 25 -- tests/03_bugs/10_break_stack_mismatch | 38 --- tests/03_bugs/11_switch_stack_mismatch | 39 --- tests/03_bugs/12_altblock_stack_mismatch | 83 ------ tests/custom/00_syntax/00_single_line_comments | 15 + tests/custom/00_syntax/01_unterminated_comment | 15 + tests/custom/00_syntax/02_multi_line_comments | 12 + tests/custom/00_syntax/03_expression_blocks | 11 + tests/custom/00_syntax/04_statement_blocks | 20 ++ tests/custom/00_syntax/05_block_nesting | 24 ++ tests/custom/00_syntax/06_open_statement_block | 13 + .../00_syntax/07_embedded_single_line_comments | 21 ++ .../00_syntax/08_embedded_multi_line_comments | 24 ++ tests/custom/00_syntax/09_string_literals | 50 ++++ tests/custom/00_syntax/10_numeric_literals | 26 ++ tests/custom/00_syntax/11_misc_literals | 17 ++ tests/custom/00_syntax/12_block_whitespace_control | 47 +++ tests/custom/00_syntax/13_object_literals | 174 +++++++++++ tests/custom/00_syntax/14_array_literals | 82 ++++++ tests/custom/00_syntax/15_function_declarations | 164 +++++++++++ tests/custom/00_syntax/16_for_loop | 299 +++++++++++++++++++ tests/custom/00_syntax/17_while_loop | 71 +++++ tests/custom/00_syntax/18_if_condition | 121 ++++++++ tests/custom/00_syntax/19_arrow_functions | 124 ++++++++ tests/custom/00_syntax/20_list_expressions | 45 +++ tests/custom/00_syntax/21_regex_literals | 89 ++++++ tests/custom/01_arithmetic/00_value_conversion | 125 ++++++++ tests/custom/01_arithmetic/01_division | 53 ++++ tests/custom/01_arithmetic/02_modulo | 32 ++ tests/custom/01_arithmetic/03_bitwise | 54 ++++ tests/custom/01_arithmetic/04_inc_dec | 49 ++++ tests/custom/02_runtime/00_scoping | 161 ++++++++++ tests/custom/02_runtime/01_break_continue | 50 ++++ tests/custom/02_runtime/02_this | 50 ++++ tests/custom/02_runtime/03_try_catch | 138 +++++++++ tests/custom/02_runtime/04_switch_case | 325 +++++++++++++++++++++ tests/custom/02_runtime/05_closure_scope | 35 +++ tests/custom/02_runtime/06_recursion | 59 ++++ tests/custom/03_bugs/01_try_catch_stack_mismatch | 52 ++++ tests/custom/03_bugs/02_array_pop_use_after_free | 14 + .../03_bugs/03_switch_fallthrough_miscompilation | 16 + tests/custom/03_bugs/04_property_set_abort | 76 +++++ tests/custom/03_bugs/05_duplicate_ressource_type | 31 ++ tests/custom/03_bugs/06_lexer_escape_at_boundary | 12 + tests/custom/03_bugs/07_lexer_overlong_lines | 13 + .../03_bugs/08_compiler_arrow_fn_expressions | 15 + .../custom/03_bugs/09_reject_invalid_array_indexes | 25 ++ tests/custom/03_bugs/10_break_stack_mismatch | 38 +++ tests/custom/03_bugs/11_switch_stack_mismatch | 39 +++ tests/custom/03_bugs/12_altblock_stack_mismatch | 83 ++++++ tests/custom/CMakeLists.txt | 7 + tests/custom/run_tests.sh | 179 ++++++++++++ 95 files changed, 3195 insertions(+), 3187 deletions(-) delete mode 100755 run_tests.sh delete mode 100644 tests/00_syntax/00_single_line_comments delete mode 100644 tests/00_syntax/01_unterminated_comment delete mode 100644 tests/00_syntax/02_multi_line_comments delete mode 100644 tests/00_syntax/03_expression_blocks delete mode 100644 tests/00_syntax/04_statement_blocks delete mode 100644 tests/00_syntax/05_block_nesting delete mode 100644 tests/00_syntax/06_open_statement_block delete mode 100644 tests/00_syntax/07_embedded_single_line_comments delete mode 100644 tests/00_syntax/08_embedded_multi_line_comments delete mode 100644 tests/00_syntax/09_string_literals delete mode 100644 tests/00_syntax/10_numeric_literals delete mode 100644 tests/00_syntax/11_misc_literals delete mode 100644 tests/00_syntax/12_block_whitespace_control delete mode 100644 tests/00_syntax/13_object_literals delete mode 100644 tests/00_syntax/14_array_literals delete mode 100644 tests/00_syntax/15_function_declarations delete mode 100644 tests/00_syntax/16_for_loop delete mode 100644 tests/00_syntax/17_while_loop delete mode 100644 tests/00_syntax/18_if_condition delete mode 100644 tests/00_syntax/19_arrow_functions delete mode 100644 tests/00_syntax/20_list_expressions delete mode 100644 tests/00_syntax/21_regex_literals delete mode 100644 tests/01_arithmetic/00_value_conversion delete mode 100644 tests/01_arithmetic/01_division delete mode 100644 tests/01_arithmetic/02_modulo delete mode 100644 tests/01_arithmetic/03_bitwise delete mode 100644 tests/01_arithmetic/04_inc_dec delete mode 100644 tests/02_runtime/00_scoping delete mode 100644 tests/02_runtime/01_break_continue delete mode 100644 tests/02_runtime/02_this delete mode 100644 tests/02_runtime/03_try_catch delete mode 100644 tests/02_runtime/04_switch_case delete mode 100644 tests/02_runtime/05_closure_scope delete mode 100644 tests/02_runtime/06_recursion delete mode 100644 tests/03_bugs/01_try_catch_stack_mismatch delete mode 100644 tests/03_bugs/02_array_pop_use_after_free delete mode 100644 tests/03_bugs/03_switch_fallthrough_miscompilation delete mode 100644 tests/03_bugs/04_property_set_abort delete mode 100644 tests/03_bugs/05_duplicate_ressource_type delete mode 100644 tests/03_bugs/06_lexer_escape_at_boundary delete mode 100644 tests/03_bugs/07_lexer_overlong_lines delete mode 100644 tests/03_bugs/08_compiler_arrow_fn_expressions delete mode 100644 tests/03_bugs/09_reject_invalid_array_indexes delete mode 100644 tests/03_bugs/10_break_stack_mismatch delete mode 100644 tests/03_bugs/11_switch_stack_mismatch delete mode 100644 tests/03_bugs/12_altblock_stack_mismatch create mode 100644 tests/custom/00_syntax/00_single_line_comments create mode 100644 tests/custom/00_syntax/01_unterminated_comment create mode 100644 tests/custom/00_syntax/02_multi_line_comments create mode 100644 tests/custom/00_syntax/03_expression_blocks create mode 100644 tests/custom/00_syntax/04_statement_blocks create mode 100644 tests/custom/00_syntax/05_block_nesting create mode 100644 tests/custom/00_syntax/06_open_statement_block create mode 100644 tests/custom/00_syntax/07_embedded_single_line_comments create mode 100644 tests/custom/00_syntax/08_embedded_multi_line_comments create mode 100644 tests/custom/00_syntax/09_string_literals create mode 100644 tests/custom/00_syntax/10_numeric_literals create mode 100644 tests/custom/00_syntax/11_misc_literals create mode 100644 tests/custom/00_syntax/12_block_whitespace_control create mode 100644 tests/custom/00_syntax/13_object_literals create mode 100644 tests/custom/00_syntax/14_array_literals create mode 100644 tests/custom/00_syntax/15_function_declarations create mode 100644 tests/custom/00_syntax/16_for_loop create mode 100644 tests/custom/00_syntax/17_while_loop create mode 100644 tests/custom/00_syntax/18_if_condition create mode 100644 tests/custom/00_syntax/19_arrow_functions create mode 100644 tests/custom/00_syntax/20_list_expressions create mode 100644 tests/custom/00_syntax/21_regex_literals create mode 100644 tests/custom/01_arithmetic/00_value_conversion create mode 100644 tests/custom/01_arithmetic/01_division create mode 100644 tests/custom/01_arithmetic/02_modulo create mode 100644 tests/custom/01_arithmetic/03_bitwise create mode 100644 tests/custom/01_arithmetic/04_inc_dec create mode 100644 tests/custom/02_runtime/00_scoping create mode 100644 tests/custom/02_runtime/01_break_continue create mode 100644 tests/custom/02_runtime/02_this create mode 100644 tests/custom/02_runtime/03_try_catch create mode 100644 tests/custom/02_runtime/04_switch_case create mode 100644 tests/custom/02_runtime/05_closure_scope create mode 100644 tests/custom/02_runtime/06_recursion create mode 100644 tests/custom/03_bugs/01_try_catch_stack_mismatch create mode 100644 tests/custom/03_bugs/02_array_pop_use_after_free create mode 100644 tests/custom/03_bugs/03_switch_fallthrough_miscompilation create mode 100644 tests/custom/03_bugs/04_property_set_abort create mode 100644 tests/custom/03_bugs/05_duplicate_ressource_type create mode 100644 tests/custom/03_bugs/06_lexer_escape_at_boundary create mode 100644 tests/custom/03_bugs/07_lexer_overlong_lines create mode 100644 tests/custom/03_bugs/08_compiler_arrow_fn_expressions create mode 100644 tests/custom/03_bugs/09_reject_invalid_array_indexes create mode 100644 tests/custom/03_bugs/10_break_stack_mismatch create mode 100644 tests/custom/03_bugs/11_switch_stack_mismatch create mode 100644 tests/custom/03_bugs/12_altblock_stack_mismatch create mode 100644 tests/custom/CMakeLists.txt create mode 100755 tests/custom/run_tests.sh diff --git a/run_tests.sh b/run_tests.sh deleted file mode 100755 index 69010c5..0000000 --- a/run_tests.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash - -line='........................................' - -extract_sections() { - local file=$1 - local dir=$2 - local count=0 - local tag line outfile - - while IFS= read -r line; do - case "$line" in - "-- Testcase --") - tag="test" - count=$((count + 1)) - outfile=$(printf "%s/%03d.in" "$dir" $count) - printf "" > "$outfile" - ;; - "-- Expect stdout --"|"-- Expect stderr --"|"-- Expect exitcode --") - tag="${line#-- Expect }" - tag="${tag% --}" - count=$((count + 1)) - outfile=$(printf "%s/%03d.%s" "$dir" $count "$tag") - printf "" > "$outfile" - ;; - "-- End --") - tag="" - outfile="" - ;; - *) - if [ -n "$tag" ]; then - printf "%s\\n" "$line" >> "$outfile" - fi - ;; - esac - done < "$file" - - return $(ls -l "$dir/"*.in 2>/dev/null | wc -l) -} - -run_testcase() { - local num=$1 - local dir=$2 - local in=$3 - local out=$4 - local err=$5 - local code=$6 - local fail=0 - - ./ucode -e '{ "REQUIRE_SEARCH_PATH": [ "./lib/*.so" ] }' -i - <"$in" >"$dir/res.out" 2>"$dir/res.err" - - touch "$dir/empty" - printf "%d\n" $? > "$dir/res.code" - - if ! cmp -s "$dir/res.err" "${err:-$dir/empty}"; then - [ $fail = 0 ] && printf "!\n" - printf "Testcase #%d: Expected stderr did not match:\n" $num - diff -u --color=always --label="Expected stderr" --label="Resulting stderr" "${err:-$dir/empty}" "$dir/res.err" - printf -- "---\n" - fail=1 - fi - - if ! cmp -s "$dir/res.out" "${out:-$dir/empty}"; then - [ $fail = 0 ] && printf "!\n" - printf "Testcase #%d: Expected stdout did not match:\n" $num - diff -u --color=always --label="Expected stdout" --label="Resulting stdout" "${out:-$dir/empty}" "$dir/res.out" - printf -- "---\n" - fail=1 - fi - - if [ -n "$code" ] && ! cmp -s "$dir/res.code" "$code"; then - [ $fail = 0 ] && printf "!\n" - printf "Testcase #%d: Expected exit code did not match:\n" $num - diff -u --color=always --label="Expected code" --label="Resulting code" "$code" "$dir/res.code" - printf -- "---\n" - fail=1 - fi - - return $fail -} - -run_test() { - local file=$1 - local name=${file##*/} - local res ecode eout eerr ein tests - local testcase_first=0 failed=0 count=0 - - printf "%s %s " "$name" "${line:${#name}}" - - mkdir "/tmp/test.$$" - - extract_sections "$file" "/tmp/test.$$" - tests=$? - - [ -f "/tmp/test.$$/001.in" ] && testcase_first=1 - - for res in "/tmp/test.$$/"[0-9]*; do - case "$res" in - *.in) - count=$((count + 1)) - - if [ $testcase_first = 1 ]; then - # Flush previous test - if [ -n "$ein" ]; then - run_testcase $count "/tmp/test.$$" "$ein" "$eout" "$eerr" "$ecode" || failed=$((failed + 1)) - eout="" - eerr="" - ecode="" - fi - - ein=$res - else - run_testcase $count "/tmp/test.$$" "$res" "$eout" "$eerr" "$ecode" || failed=$((failed + 1)) - - eout="" - eerr="" - ecode="" - fi - - ;; - *.stdout) eout=$res ;; - *.stderr) eerr=$res ;; - *.exitcode) ecode=$res ;; - esac - done - - # Flush last test - if [ $testcase_first = 1 ] && [ -n "$eout$eerr$ecode" ]; then - run_testcase $count "/tmp/test.$$" "$ein" "$eout" "$eerr" "$ecode" || failed=$((failed + 1)) - fi - - rm -r "/tmp/test.$$" - - if [ $failed = 0 ]; then - printf "OK\n" - else - printf "%s %s FAILED (%d/%d)\n" "$name" "${line:${#name}}" $failed $tests - fi - - return $failed -} - - -n_tests=0 -n_fails=0 - -select_tests="$@" - -use_test() { - local input="$(readlink -f "$1")" - local test - - [ -f "$input" ] || return 1 - [ -n "$select_tests" ] || return 0 - - for test in "$select_tests"; do - test="$(readlink -f "$test")" - - [ "$test" != "$input" ] || return 0 - done - - return 1 -} - -for catdir in tests/[0-9][0-9]_*; do - [ -d "$catdir" ] || continue - - printf "\n##\n## Running %s tests\n##\n\n" "${catdir##*/[0-9][0-9]_}" - - for testfile in "$catdir/"[0-9][0-9]_*; do - use_test "$testfile" || continue - - n_tests=$((n_tests + 1)) - run_test "$testfile" || n_fails=$((n_fails + 1)) - done -done - -printf "\nRan %d tests, %d okay, %d failures\n" $n_tests $((n_tests - n_fails)) $n_fails diff --git a/tests/00_syntax/00_single_line_comments b/tests/00_syntax/00_single_line_comments deleted file mode 100644 index 24a32a2..0000000 --- a/tests/00_syntax/00_single_line_comments +++ /dev/null @@ -1,15 +0,0 @@ -Single line comments. - --- Expect stdout -- -This is a test. - a test. -A test . - --- End -- - --- Testcase -- -This is {# a comment within #} a test. -{# Comment before #} a test. -A test {# and a comment after #}. -{# This is a comment with embedded "{#" tag. #} --- End -- diff --git a/tests/00_syntax/01_unterminated_comment b/tests/00_syntax/01_unterminated_comment deleted file mode 100644 index 1d3669c..0000000 --- a/tests/00_syntax/01_unterminated_comment +++ /dev/null @@ -1,15 +0,0 @@ -Unterminated comment - --- Expect stderr -- -Syntax error: Unterminated template block -In line 1, byte 9: - - `This is {# an unclosed comment` - ^-- Near here - - --- End -- - --- Testcase -- -This is {# an unclosed comment --- End -- diff --git a/tests/00_syntax/02_multi_line_comments b/tests/00_syntax/02_multi_line_comments deleted file mode 100644 index 99fc37e..0000000 --- a/tests/00_syntax/02_multi_line_comments +++ /dev/null @@ -1,12 +0,0 @@ -Multiline comments. - --- Expect stdout -- -This is an example text for testing comment blocks. --- End -- - --- Testcase -- -This is an example text {# containing -a comment spanning multiple lines and - different indentation - #} for testing comment blocks. --- End -- diff --git a/tests/00_syntax/03_expression_blocks b/tests/00_syntax/03_expression_blocks deleted file mode 100644 index 3568016..0000000 --- a/tests/00_syntax/03_expression_blocks +++ /dev/null @@ -1,11 +0,0 @@ -Testing expression blocks. - --- Expect stdout -- -The result of 3 * 7 is 21. -To escape the start tag, output it as string expression: {{ --- End -- - --- Testcase -- -The result of 3 * 7 is {{ 3 * 7 }}. -To escape the start tag, output it as string expression: {{ "{{" }} --- End -- diff --git a/tests/00_syntax/04_statement_blocks b/tests/00_syntax/04_statement_blocks deleted file mode 100644 index 920ed71..0000000 --- a/tests/00_syntax/04_statement_blocks +++ /dev/null @@ -1,20 +0,0 @@ -Testing statement blocks. - --- Expect stdout -- -The result of 3 * 7 is 21. -A statement block may contain multiple statements: Hello World -To escape the start tag, output it as string expression: {% -Alternatively print it: {% --- End -- - --- Testcase -- -The result of 3 * 7 is {%+ print(3 * 7) %}. -A statement block may contain multiple statements: {%+ - print("Hello "); - print("World"); -%} - -To escape the start tag, output it as string expression: {{ "{%" }} -Alternatively print it: {%+ print("{%") %} - --- End -- diff --git a/tests/00_syntax/05_block_nesting b/tests/00_syntax/05_block_nesting deleted file mode 100644 index fcfd7da..0000000 --- a/tests/00_syntax/05_block_nesting +++ /dev/null @@ -1,24 +0,0 @@ -Nesting blocks into non-comment blocks should fail. - --- Expect stderr -- -Syntax error: Template blocks may not be nested -In line 2, byte 61: - - `We may not nest statement blocks into expression blocks: {{ {% print(1 + 2) %} }}.` - Near here --------------------------------------------------^ - - -Syntax error: Template blocks may not be nested -In line 3, byte 61: - - `We may not nest expression blocks into statement blocks: {% {{ 1 + 2 }} %}.` - Near here --------------------------------------------------^ - - --- End -- - --- Testcase -- -We can nest other block types into comments: {# {% {{ 1 + 2 }} %} #} -We may not nest statement blocks into expression blocks: {{ {% print(1 + 2) %} }}. -We may not nest expression blocks into statement blocks: {% {{ 1 + 2 }} %}. --- End -- diff --git a/tests/00_syntax/06_open_statement_block b/tests/00_syntax/06_open_statement_block deleted file mode 100644 index 9c2d142..0000000 --- a/tests/00_syntax/06_open_statement_block +++ /dev/null @@ -1,13 +0,0 @@ -The last statement block of a template may remain open, this is useful for templates -that contain only code. - --- Expect stdout -- -This template consists entirely of script code! --- End -- - --- Testcase -- -{% - print("This template "); - print("consists entirely "); - print("of script code!\n"); --- End -- diff --git a/tests/00_syntax/07_embedded_single_line_comments b/tests/00_syntax/07_embedded_single_line_comments deleted file mode 100644 index 43f188c..0000000 --- a/tests/00_syntax/07_embedded_single_line_comments +++ /dev/null @@ -1,21 +0,0 @@ -Statement and expression blocks may contain C++-style comments. -A C++-style comment is started by two subsequent slashes and spans -until the next newline. - --- Expect stdout -- -The result of 5 + 9 is 14. -Statement blocks may use C++ comments too: Test Another test. --- End -- - --- Testcase -- -The result of 5 + 9 is {{ // The block end tag is ignored: }} -// And the expression block continues here! -5 + 9 }}. -Statement blocks may use C++ comments too: {%+ - print("Test"); // A comment. - - // Another comment. - print(" Another test."); -%} - --- End -- diff --git a/tests/00_syntax/08_embedded_multi_line_comments b/tests/00_syntax/08_embedded_multi_line_comments deleted file mode 100644 index 75aba5f..0000000 --- a/tests/00_syntax/08_embedded_multi_line_comments +++ /dev/null @@ -1,24 +0,0 @@ -Statement and expression blocks may contain C-style comments. -A C-style comment is started by a slash followed by an asterisk -and ended by an asterisk followed by a slash. - -Such comments may appear everywhere within statement or expression -blocks, even in the middle of statements or expressions. - --- Expect stdout -- -The result of 12 - 4 is 8. -Statement blocks may use C comments too: Test Another test. The final test. --- End -- - --- Testcase -- -The result of 12 - 4 is {{ /* A comment before */ 12 - /* or even within */ 4 /* or after an expression */ }}. -Statement blocks may use C comments too: {%+ - print("Test"); /* A comment. */ - - /* Another comment. */ - print(" Another test."); - - print(/* A comment within */ " The final test."); -%} - --- End -- diff --git a/tests/00_syntax/09_string_literals b/tests/00_syntax/09_string_literals deleted file mode 100644 index 0967850..0000000 --- a/tests/00_syntax/09_string_literals +++ /dev/null @@ -1,50 +0,0 @@ -String literals may be enclosed in single or double quotes. -Embedded escape sequences are started with a backslash, followed -by either a hexadecimal, an octal or a single character escape sequence. - --- Expect stdout -- -Single quoted string -Double quoted string -Unicode escape sequence: ☀💩 -Escaped double quote (") character -Escaped single quote (') character -Hexadecimal escape: XYZ xyz -Octal escape: ABC xyz -{ "Single char escape": "\u0007\b\u001b\f\r\t\u000b\\\n" } --- End -- - --- Testcase -- -{{ 'Single quoted string' }} -{{ "Double quoted string" }} -{{ "Unicode escape sequence: \u2600\uD83D\uDCA9" }} -{{ "Escaped double quote (\") character" }} -{{ 'Escaped single quote (\') character' }} -{{ "Hexadecimal escape: \x58\x59\x5A \x78\x79\x7a" }} -{{ "Octal escape: \101\102\103 \170\171\172" }} -{{ { "Single char escape": "\a\b\e\f\r\t\v\\\n" } }} --- End -- - - -Testing various parsing corner cases. - --- Expect stdout -- -[ "\t", "\n", "y", "\u0001", "\n", "\u0001\u0002", "\u0001\u0002", "\u0001\u0002", "\u0001a", "\na" ] --- End -- - --- Testcase -- -{% - print([ - "\ ", // properly handle escaped tab - "\ -", // properly handle escaped newline - "\y", // substitute unrecognized escape with escaped char - "\1", // handle short octal sequence at end of string - "\12", // handle short octal sequence at end of string - "\1\2", // handle subsequent short octal sequences - "\001\2", // handle short sequence after long one - "\1\002", // handle long sequence after short one - "\1a", // handle short octal sequence terminated by non-octal char - "\12a" // handle short octal sequence terminated by non-octal char - ], "\n"); -%} --- End -- diff --git a/tests/00_syntax/10_numeric_literals b/tests/00_syntax/10_numeric_literals deleted file mode 100644 index 3e367d0..0000000 --- a/tests/00_syntax/10_numeric_literals +++ /dev/null @@ -1,26 +0,0 @@ -C-style numeric integer and float literals are understood, as well -as the special keywords "Infinity" and "NaN" to denote the IEEE 754 -floating point values. - -Numeric values are either stored as signed 64 bit integers or signed -doubles internally. - --- Expect stdout -- -Integers literals: 123, 127, 2748, 57082 -Float literals: 10, 10.3, 1.23456e-65, 16.0625 -Special values: Infinity, Infinity, NaN, NaN -Minimum values: -9223372036854775808, -1.79769e+308 -Maximum values: 9223372036854775807, 1.79769e+308 -Minimum truncation: -9223372036854775808, -Infinity -Maximum truncation: 9223372036854775807, Infinity --- End -- - --- Testcase -- -Integers literals: {{ 123 }}, {{ 0177 }}, {{ 0xabc }}, {{ 0xDEFA }} -Float literals: {{ 10. }}, {{ 10.3 }}, {{ 123.456e-67 }}, {{ 0x10.1 }} -Special values: {{ Infinity }}, {{ 1 / 0 }}, {{ NaN }}, {{ "x" / 1 }} -Minimum values: {{ -9223372036854775808 }}, {{ -1.7976931348623158e+308 }} -Maximum values: {{ 9223372036854775807 }}, {{ 1.7976931348623158e+308 }} -Minimum truncation: {{ -10000000000000000000 }}, {{ -1.0e309 }} -Maximum truncation: {{ 10000000000000000000 }}, {{ 1.0e309 }} --- End -- diff --git a/tests/00_syntax/11_misc_literals b/tests/00_syntax/11_misc_literals deleted file mode 100644 index 372741c..0000000 --- a/tests/00_syntax/11_misc_literals +++ /dev/null @@ -1,17 +0,0 @@ -The utpl script language features a number of keywords which represent -certain special values. - --- Expect stdout -- -The "this" keyword refers to the current function context: object -The "null" keyword represents the null value: null -The "true" keyword represents a true boolean value: true -The "false" keyword represents a false boolean value: false --- End -- - --- Testcase -- -{% let t = { f: function() { return this; } } %} -The "this" keyword refers to the current function context: {{ type(t.f()) }} -The "null" keyword represents the null value: {{ "" + null }} -The "true" keyword represents a true boolean value: {{ true }} -The "false" keyword represents a false boolean value: {{ false }} --- End -- diff --git a/tests/00_syntax/12_block_whitespace_control b/tests/00_syntax/12_block_whitespace_control deleted file mode 100644 index 911171c..0000000 --- a/tests/00_syntax/12_block_whitespace_control +++ /dev/null @@ -1,47 +0,0 @@ -By default, whitespace before a block start tag or after a block end tag -is retained. By suffixing the start tag or prefixing the end tag with a -dash, the leading or trailing whitespace is trimmed respectively. - --- Expect stdout -- -Whitespace control applies to all block types: -Comment before: | |, after: | |, both: || -Statement before: |test |, after: | test|, both: |test| -Expression before: |test |, after: | test|, both: |test| - -By default whitespace around a block is retained. -Leading whitespace can be trimmed like this. -The same applies to trailing whitespace. -It is also possible to trim bothleading and trailingwhitespace. - -Stripping works across multiple lines as well:test - -Likewise, stripping over multiple lines of trailing whitespace works as -expected too.This is after the block. --- End -- - --- Testcase -- -Whitespace control applies to all block types: -Comment before: | {#- test #} |, after: | {#- test #} |, both: | {#- test -#} | -Statement before: | {%- print("test") %} |, after: | {%+ print("test") -%} |, both: | {%- print("test") -%} | -Expression before: | {{- "test" }} |, after: | {{ "test" -}} |, both: | {{- "test" -}} | - -By default whitespace {{ "around a block" }} is retained. -Leading whitespace can be trimmed {#- note the leading dash #} like this. -The same applies to {# note the trailing dash -#} trailing whitespace. -It is also possible to trim both {{- "leading and trailing" -}} whitespace. - -Stripping works across multiple lines as well: - -{%- - /* The word "test" will be printed after "well:" above */ - print("test") -%} - - -Likewise, stripping over multiple lines of trailing whitespace works as -expected too. - -{#- Any whitespace after "expected too." and before "This is after the block" will be trimmed. -#} - -This is after the block. --- End -- diff --git a/tests/00_syntax/13_object_literals b/tests/00_syntax/13_object_literals deleted file mode 100644 index 18fbbed..0000000 --- a/tests/00_syntax/13_object_literals +++ /dev/null @@ -1,174 +0,0 @@ -The utpl script language supports declaring objects (dictionaries) using -either JSON or JavaScript notation. - --- Expect stdout -- -{ } -{ "name": "Bob", "age": 31, "email": { "work": "bob@example.com", "private": "bob@example.org" } } -{ "banana": "yellow", "tomato": "red", "broccoli": "green" } -{ "foo": "bar", "complex key": "qrx" } -{ "foo": { "bar": true } } --- End -- - --- Testcase -- -{% - // An empty object can be declared using a pair of curly brackets - empty_obj = { }; - - // It is also possible to use JSON notation to declare an object - json_obj = { - "name": "Bob", - "age": 31, - "email": { - "work": "bob@example.com", - "private": "bob@example.org" - } - }; - - // Declaring an object in JavaScript notation is supported as well - another_obj = { - banana: "yellow", - tomato: "red", - broccoli: "green" - }; - - // Mixing styles is allowed too - third_obj = { - foo: "bar", - "complex key": "qrx" - }; - - // Important caveat: when nesting objects, ensure that curly brackets - // are separated by space or newline to avoid interpretation as - // expression block tag! - nested_obj = { foo: { bar: true } }; // <-- mind the space in "} }" - - // Printing (or stringifying) objects will return their JSON representation - print(empty_obj, "\n"); - print(json_obj, "\n"); - print(another_obj, "\n"); - print(third_obj, "\n"); - print(nested_obj, "\n"); -%} --- End -- - - -Additionally, utpl implements ES6-like spread operators to allow shallow copying -of object properties into other objects. - --- Expect stdout -- -{ "foo": true, "bar": false } -{ "foo": true, "bar": false, "baz": 123, "qrx": 456 } -{ "foo": false, "bar": true, "baz": 123, "qrx": 456 } -{ "foo": true, "bar": false } -{ "foo": true, "bar": false, "level2": { "baz": 123, "qrx": 456 } } -{ "foo": true, "bar": false, "0": 7, "1": 8, "2": 9 } --- End -- - --- Testcase -- -{% - o1 = { foo: true, bar: false }; - o2 = { baz: 123, qrx: 456 }; - arr = [7, 8, 9]; - - print(join("\n", [ - // copying one object into another - { ...o1 }, - - // combining two objects - { ...o1, ...o2 }, - - // copying object and override properties - { ...o1, ...o2, foo: false, bar: true }, - - // default properties overwritten by spread operator - { foo: 123, bar: 456, ...o1 }, - - // nested spread operators - { ...o1, level2: { ...o2 } }, - - // merging array into objects - { ...o1, ...arr } - ]), "\n"); -%} --- End -- - - -ES2015 short hand property notation is supported as well. - --- Expect stdout -- -{ "a": 123, "b": true, "c": "test" } --- End -- - --- Testcase -- -{% - a = 123; - b = true; - c = "test"; - - o = { a, b, c }; - - print(o, "\n"); -%} --- End -- - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ':' -In line 2, byte 14: - - ` o = { "foo" };` - Near here ------^ - - --- End -- - --- Testcase -- -{% - o = { "foo" }; -%} --- End -- - - -ES2015 computed property names are supported. - --- Expect stdout -- -{ "test": true, "hello": false, "ABC": 123 } --- End -- - --- Testcase -- -{% - s = "test"; - o = { - [s]: true, - ["he" + "llo"]: false, - [uc("abc")]: 123 - }; - - print(o, "\n"); -%} --- End -- - --- Expect stderr -- -Syntax error: Expecting expression -In line 2, byte 10: - - ` o1 = { []: true };` - Near here --^ - - -Syntax error: Unexpected token -Expecting ']' -In line 3, byte 14: - - ` o2 = { [true, false]: 123 };` - Near here ------^ - - --- End -- - --- Testcase -- -{% - o1 = { []: true }; - o2 = { [true, false]: 123 }; -%} --- End -- diff --git a/tests/00_syntax/14_array_literals b/tests/00_syntax/14_array_literals deleted file mode 100644 index 941ee4a..0000000 --- a/tests/00_syntax/14_array_literals +++ /dev/null @@ -1,82 +0,0 @@ -The utpl script language supports declaring arrays using JSON notation. - --- Expect stdout -- -[ ] -[ "first", "second", 123, [ "a", "nested", "array" ], { "a": "nested object" } ] --- End -- - --- Testcase -- -{% - // An empty array can be declared using a pair of square brackets - empty_array = [ ]; - - // JSON notation is used to declare an array with contents - json_array = [ - "first", - "second", - 123, - [ "a", "nested", "array" ], - { a: "nested object" } - ]; - - // Printing (or stringifying) arrays will return their JSON representation - print(empty_array, "\n"); - print(json_array, "\n"); --- End -- - - -Additionally, utpl implements ES6-like spread operators to allow shallow copying -of array values into other arrays. - --- Expect stdout -- -[ 1, 2, 3 ] -[ 1, 2, 3, 4, 5, 6 ] -[ 1, 2, 3, 4, 5, 6, false, true ] -[ 1, 2, 3, false, true, 4, 5, 6 ] -[ 1, 2, 3, [ 4, 5, 6 ] ] --- End -- - --- Testcase -- -{% - a1 = [ 1, 2, 3 ]; - a2 = [ 4, 5, 6 ]; - - print(join("\n", [ - // copying one array into another - [ ...a1 ], - - // combining two arrays - [ ...a1, ...a2 ], - - // copying array and append values - [ ...a1, ...a2, false, true ], - - // copy array and interleave values - [ ...a1, false, true, ...a2 ], - - // nested spread operators - [ ...a1, [ ...a2 ] ] - ]), "\n"); -%} --- End -- - -Contrary to merging arrays into objects, objects cannot be merged into arrays. - --- Expect stderr -- -Type error: ({ "foo": true, "bar": false }) is not iterable -In line 5, byte 21: - - ` print([ ...arr, ...obj ], "\n");` - Near here -------------^ - - --- End -- - --- Testcase -- -{% - arr = [ 1, 2, 3 ]; - obj = { foo: true, bar: false }; - - print([ ...arr, ...obj ], "\n"); -%} --- End -- diff --git a/tests/00_syntax/15_function_declarations b/tests/00_syntax/15_function_declarations deleted file mode 100644 index 4257dd6..0000000 --- a/tests/00_syntax/15_function_declarations +++ /dev/null @@ -1,164 +0,0 @@ -Function declarations follow the ECMAScript 5 syntax. Functions can be -declared anonymously, which is useful for "throw-away" functions such -as sort or filter callbacks or for building objects or arrays of function -values. - -If functions are declared with a name, the resulting function value is -automatically assigned under the given name to the current scope. - -When function values are stringifed, the resulting string will describe -the declaration of the function. - -Nesting function declarations is possible as well. - - --- Expect stdout -- -function() { ... } -function test_fn(a, b) { ... } -function test2_fn(a, b) { ... } - -A function declaration using the alternative syntax: -The function was called with arguments 123 and 456. - --- End -- - --- Testcase -- -{% - // declare an anonymous function and - // assign resulting value - anon_fn = function() { - return "test"; - }; - - // declare a named function - function test_fn(a, b) { - return a + b; - } - - // nesting functions is legal - function test2_fn(a, b) { - function test3_fn(a, b) { - return a * b; - } - - return a + test3_fn(a, b); - } - - print(anon_fn, "\n"); - print(test_fn, "\n"); - print(test2_fn, "\n"); -%} - -A function declaration using the alternative syntax: -{% function test3_fn(a, b): %} -The function was called with arguments {{ a }} and {{ b }}. -{% endfunction %} -{{ test3_fn(123, 456) }} --- End -- - - -Additionally, utpl implements ES6-like "rest" argument syntax to declare -variadic functions. - --- Expect stdout -- -function non_variadic(a, b, c, d, e) { ... } -[ 1, 2, 3, 4, 5 ] -function variadic_1(a, b, ...args) { ... } -[ 1, 2, [ 3, 4, 5 ] ] -function variadic_2(...args) { ... } -[ [ 1, 2, 3, 4, 5 ] ] --- End -- - --- Testcase -- -{% - // ordinary, non-variadic function - function non_variadic(a, b, c, d, e) { - return [ a, b, c, d, e ]; - } - - // fixed amount of arguments with variable remainder - function variadic_1(a, b, ...args) { - return [ a, b, args ]; - } - - // only variable arguments - function variadic_2(...args) { - return [ args ]; - } - - print(join("\n", [ - non_variadic, - non_variadic(1, 2, 3, 4, 5), - variadic_1, - variadic_1(1, 2, 3, 4, 5), - variadic_2, - variadic_2(1, 2, 3, 4, 5) - ]), "\n"); -%} --- End -- - - -Complementary to the "rest" argument syntax, the spread operator may be -used in function call arguments to pass arrays of values as argument list. - --- Expect stdout -- -[ 1, 2, 3, 4, 5, 6 ] -[ 4, 5, 6, 1, 2, 3 ] -[ 1, 2, 3, 1, 2, 3 ] -[ 1, 2, 3 ] --- End -- - --- Testcase -- -{% - arr = [ 1, 2, 3 ]; - - function test(...args) { - return args; - } - - print(join("\n", [ - test(...arr, 4, 5, 6), - test(4, 5, 6, ...arr), - test(...arr, ...arr), - test(...arr) - ]), "\n"); -%} --- End -- - - -Rest arguments may be only used once in a declaration and they must always -be the last item in the argument list. - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ')' -In line 2, byte 26: - - ` function illegal(...args, ...args2) {}` - Near here ------------------^ - - --- End -- - --- Testcase -- -{% - function illegal(...args, ...args2) {} -%} --- End -- - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ')' -In line 2, byte 26: - - ` function illegal(...args, a, b) {}` - Near here ------------------^ - - --- End -- - --- Testcase -- -{% - function illegal(...args, a, b) {} -%} --- End -- diff --git a/tests/00_syntax/16_for_loop b/tests/00_syntax/16_for_loop deleted file mode 100644 index 67edc21..0000000 --- a/tests/00_syntax/16_for_loop +++ /dev/null @@ -1,299 +0,0 @@ -Two for-loop variants are supported: a C-style counting for loop -consisting of an initialization expression, a test condition -and a step expression and a for-in-loop variant which allows -enumerating properties of objects or items of arrays. - -Additionally, utpl supports an alternative syntax suitable for -template block tags. - - --- Expect stdout -- -A simple counting for-loop: -Iteration 0 -Iteration 1 -Iteration 2 -Iteration 3 -Iteration 4 -Iteration 5 -Iteration 6 -Iteration 7 -Iteration 8 -Iteration 9 - -If the loop body consists of only one statement, the curly braces -may be omitted: -Iteration 0 -Iteration 1 -Iteration 2 -Iteration 3 -Iteration 4 -Iteration 5 -Iteration 6 -Iteration 7 -Iteration 8 -Iteration 9 - -Any of the init-, test- and increment expressions may be omitted. - -Loop without initialization statement: -Iteration null -Iteration 1 -Iteration 2 - -Loop without test statement: -Iteration 0 -Iteration 1 -Iteration 2 - -Loop without init-, test- or increment statement: -Iteration 1 -Iteration 2 -Iteration 3 - -For-in loop enumerating object properties: -Key: foo -Key: bar - -For-in loop enumerating array items: -Item: one -Item: two -Item: three - -A counting for-loop using the alternative syntax: -Iteration 0 -Iteration 1 -Iteration 2 -Iteration 3 -Iteration 4 -Iteration 5 -Iteration 6 -Iteration 7 -Iteration 8 -Iteration 9 - -A for-in loop using the alternative syntax: -Item 123 -Item 456 -Item 789 - -For-in and counting for loops may declare variables: -Iteration 0 -Iteration 1 -Iteration 2 - -Item 123 -Item 456 -Item 789 --- End -- - --- Testcase -- -A simple counting for-loop: -{% - for (i = 0; i < 10; i++) { - print("Iteration "); - print(i); - print("\n"); - } -%} - -If the loop body consists of only one statement, the curly braces -may be omitted: -{% - for (i = 0; i < 10; i++) - print("Iteration ", i, "\n"); -%} - -Any of the init-, test- and increment expressions may be omitted. - -Loop without initialization statement: -{% - for (; j < 3; j++) - print("Iteration " + j + "\n"); -%} - -Loop without test statement: -{% - for (j = 0;; j++) { - if (j == 3) - break; - - print("Iteration ", j, "\n"); - } -%} - -Loop without init-, test- or increment statement: -{% - for (;;) { - if (k++ == 3) - break; - - print("Iteration ", k, "\n"); - } -%} - -For-in loop enumerating object properties: -{% - obj = { foo: true, bar: false }; - for (key in obj) - print("Key: ", key, "\n"); -%} - -For-in loop enumerating array items: -{% - arr = [ "one", "two", "three" ]; - for (item in arr) - print("Item: ", item, "\n"); -%} - -A counting for-loop using the alternative syntax: -{% for (x = 0; x < 10; x++): -%} -Iteration {{ x }} -{% endfor %} - -A for-in loop using the alternative syntax: -{% for (n in [123, 456, 789]): -%} -Item {{ n }} -{% endfor %} - -For-in and counting for loops may declare variables: -{% for (let i = 0; i < 3; i++): %} -Iteration {{ i }} -{% endfor %} - -{% for (let n in [123, 456, 789]): %} -Item {{ n }} -{% endfor %} --- End -- - - -By specifying two loop variables in for-in loop expressions, keys -and values can be iterated simultaneously. - --- Expect stdout -- -true -false -123 -456 -[ 0, true ] -[ 1, false ] -[ 2, 123 ] -[ 3, 456 ] -foo -bar -baz -qrx -[ "foo", true ] -[ "bar", false ] -[ "baz", 123 ] -[ "qrx", 456 ] --- End -- - --- Testcase -- -{% - let arr = [ true, false, 123, 456 ]; - let obj = { foo: true, bar: false, baz: 123, qrx: 456 }; - - // iterating arrays with one loop variable yields the array values - for (let x in arr) - print(x, "\n"); - - // iterating arrays with two loop variables yields the array indexes - // and their corresponding values - for (let x, y in arr) - print([x, y], "\n"); - - // iterating objects with one loop variable yields the object keys - for (let x in obj) - print(x, "\n"); - - // iterating objects with two loop variables yields the object keys - // and their corresponding values - for (let x, y in obj) - print([x, y], "\n"); -%} --- End -- - - -Ensure that for-in loop expressions with more than two variables are -rejected. - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ';' -In line 2, byte 24: - - ` for (let x, y, z in {})` - Near here ----------------^ - - --- End -- - --- Testcase -- -{% - for (let x, y, z in {}) - ; -%} --- End -- - - -Ensure that assignments in for-in loop expressions are rejected. - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ';' -In line 2, byte 25: - - ` for (let x = 1, y in {})` - Near here -----------------^ - - --- End -- - --- Testcase -- -{% - for (let x = 1, y in {}) - ; -%} --- End -- - - -Ensure that too short for-in loop expressions are rejected (1/2). - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ';' -In line 2, byte 12: - - ` for (let x)` - Near here ----^ - - --- End -- - --- Testcase -- -{% - for (let x) - ; -%} --- End -- - - -Ensure that too short for-in loop expressions are rejected (2/2). - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ';' -In line 2, byte 15: - - ` for (let x, y)` - Near here -------^ - - --- End -- - --- Testcase -- -{% - for (let x, y) - ; -%} --- End -- diff --git a/tests/00_syntax/17_while_loop b/tests/00_syntax/17_while_loop deleted file mode 100644 index 1e68d6b..0000000 --- a/tests/00_syntax/17_while_loop +++ /dev/null @@ -1,71 +0,0 @@ -Utpl implements C-style while loops which run as long as the condition -is fulfilled. - -Like with for-loops, an alternative syntax form suitable for template -blocks is supported. - - --- Expect stdout -- -A simple counting while-loop: -Iteration 0 -Iteration 1 -Iteration 2 -Iteration 3 -Iteration 4 -Iteration 5 -Iteration 6 -Iteration 7 -Iteration 8 -Iteration 9 - -If the loop body consists of only one statement, the curly braces -may be omitted: -Iteration 0 -Iteration 1 -Iteration 2 -Iteration 3 -Iteration 4 -Iteration 5 -Iteration 6 -Iteration 7 -Iteration 8 -Iteration 9 - -A counting while-loop using the alternative syntax: -Iteration 0 -Iteration 1 -Iteration 2 -Iteration 3 -Iteration 4 -Iteration 5 -Iteration 6 -Iteration 7 -Iteration 8 -Iteration 9 --- End -- - --- Testcase -- -A simple counting while-loop: -{% - i = 0; - while (i < 10) { - print("Iteration "); - print(i); - print("\n"); - i++; - } -%} - -If the loop body consists of only one statement, the curly braces -may be omitted: -{% - i = 0; - while (i < 10) - print("Iteration ", i++, "\n"); -%} - -A counting while-loop using the alternative syntax: -{% while (x < 10): -%} -Iteration {{ "" + x++ }} -{% endwhile %} --- End -- diff --git a/tests/00_syntax/18_if_condition b/tests/00_syntax/18_if_condition deleted file mode 100644 index 9e02767..0000000 --- a/tests/00_syntax/18_if_condition +++ /dev/null @@ -1,121 +0,0 @@ -Utpl implements C-style if/else conditions and ?: ternary statements. - -Like with for- and while-loops, an alternative syntax form suitable -for template blocks is supported. - - --- Expect stdout -- -This should print "one": -one - -This should print "two": -two - -Multiple conditions can be used by chaining if/else statements: -three - -If the conditional block consists of only one statement, the curly -braces may be omitted: -two - -An if-condition using the alternative syntax: -Variable x has another value. - - -An if-condition using the special "elif" keyword in alternative syntax mode: -Variable x was set to five. - - -Ternary expressions function similar to if/else statements but -only allow for a single expression in the true and false branches: -Variable x is one --- End -- - --- Testcase -- -This should print "one": -{% - x = 0; - - if (x == 0) { - print("one"); - } - else { - print("two"); - } -%} - - -This should print "two": -{% - x = 1; - - if (x == 0) { - print("one"); - } - else { - print("two"); - } -%} - - -Multiple conditions can be used by chaining if/else statements: -{% - x = 2; - - if (x == 0) { - print("one"); - } - else if (x == 1) { - print("two"); - } - else if (x == 2) { - print("three"); - } - else { - print("four"); - } -%} - - -If the conditional block consists of only one statement, the curly -braces may be omitted: -{% - x = 5; - - if (x == 0) - print("one"); - else - print("two"); -%} - - -An if-condition using the alternative syntax: -{% if (x == 1): -%} -Variable x was set to one. -{% else -%} -Variable x has another value. -{% endif %} - - -An if-condition using the special "elif" keyword in alternative syntax mode: -{% if (x == 0): -%} -Variable x was set to zero. -{% elif (x == 1): -%} -Variable x was set to one. -{% elif (x == 5): -%} -Variable x was set to five. -{% else -%} -Variable x has another value. -{% endif %} - - -Ternary expressions function similar to if/else statements but -only allow for a single expression in the true and false branches: -{% - x = 1; - s = (x == 1) ? "Variable x is one" : "Variable x has another value"; - - print(s); -%} - --- End -- diff --git a/tests/00_syntax/19_arrow_functions b/tests/00_syntax/19_arrow_functions deleted file mode 100644 index 102c527..0000000 --- a/tests/00_syntax/19_arrow_functions +++ /dev/null @@ -1,124 +0,0 @@ -Besides the ordinary ES5-like function declarations, utpl supports ES6 inspired -arrow function syntax as well. Such arrow functions are useful for callbacks to functions such as replace(), map() or filter(). - --- Expect stdout -- -() => { ... } -test -(a, b) => { ... } -3 -(...args) => { ... } -15 -(a) => { ... } -10 -(a) => { ... } -36 --- End -- - --- Testcase -- -{% - - // assign arrow function to variable - test1_fn = () => { - return "test"; - }; - - // assign arrow function with parameters - test2_fn = (a, b) => { - return a + b; - }; - - // nesting functions is legal - test3_fn = (...args) => { - nested_fn = (a, b) => { - return a * b; - }; - - return args[0] + nested_fn(args[0], args[1]); - }; - - // parentheses may be omitted if arrow function takes only one argument - test4_fn = a => { - a * 2; - }; - - // curly braces may be omitted if function body is a single expression - test5_fn = a => a * a; - - print(join("\n", [ - test1_fn, - test1_fn(), - test2_fn, - test2_fn(1, 2), - test3_fn, - test3_fn(3, 4), - test4_fn, - test4_fn(5), - test5_fn, - test5_fn(6) - ]), "\n"); -%} --- End -- - - -While the main advantage of arrow functions is the compact syntax, another -important difference to normal functions is the "this" context behaviour - -arrow functions do not have an own "this" context and simply inherit it from -the outer calling scope. - --- Expect stdout -- -this is set to obj: true -arrow function uses outher this: true -normal function has own this: true -arrow function as method has no this: true --- End -- - --- Testcase -- -{% - obj = { - method: function() { - let that = this; - let arr = () => { - print("arrow function uses outher this: ", that == this, "\n"); - }; - let fn = function() { - print("normal function has own this: ", that != this, "\n"); - }; - - print("this is set to obj: ", this == obj, "\n"); - - arr(); - fn(); - }, - - arrowfn: () => { - print("arrow function as method has no this: ", this == null, "\n"); - } - }; - - obj.method(); - obj.arrowfn(); -%} --- End -- - - -Due to the difficulty of recognizing arrow function expressions with an LR(1) -grammar the parser has to use a generic expression rule on the lhs argument list -and verify that it does not contain non-label nodes while building the ast. The -subsequent testcase asserts that case. - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ';' -In line 2, byte 10: - - ` (a + 1) => { print("test\n") }` - Near here --^ - - --- End -- - --- Testcase -- -{% - (a + 1) => { print("test\n") } -%} --- End -- diff --git a/tests/00_syntax/20_list_expressions b/tests/00_syntax/20_list_expressions deleted file mode 100644 index d5ba459..0000000 --- a/tests/00_syntax/20_list_expressions +++ /dev/null @@ -1,45 +0,0 @@ -Similar to ES5, utpl's language grammar allows comma separated list expressions -in various contexts. Unless such lists happen to be part of a function call -or array construction expression, only the last result of such an expression -list should be used while still evaluating all sub-expressions, triggering -side effects such as function calls or variable assignments. - --- Expect stdout -- -4 -[ 1, 3 ] -{ "a": true, "b": 1 } -function call -[ "test", "assigment" ] -true -true -true -[ 2, 3 ] --- End -- - --- Testcase -- -{% - // only the last value is considered - print(1 + (2, 3), "\n"); - - // in array constructors, parenthesized lists are reduced to the last value - print([ (0, 1), (2, 3) ], "\n"); - - // in object constructors, parenthesized lists are reduced to the last value - print({ a: (false, true), b: (0, 1) }, "\n"); - - // all list expressions are evaluated and may have side effects, even if - // results are discareded - x = (print("function call\n"), y = "assigment", "test"); - print([x, y], "\n"); - - // property access operates on the last value of a parenthesized list expression - print(({foo: false}, {foo: true}).foo, "\n"); - print(({foo: false}, {foo: true})["foo"], "\n"); - - // computed property access uses the last list expression value - print(({foo: true})["bar", "baz", "foo"], "\n"); - - // same list semantics apply to function call parameters - ((...args) => print(args, "\n"))((1, 2), 3); -%} --- End -- diff --git a/tests/00_syntax/21_regex_literals b/tests/00_syntax/21_regex_literals deleted file mode 100644 index 3af53bb..0000000 --- a/tests/00_syntax/21_regex_literals +++ /dev/null @@ -1,89 +0,0 @@ -Regex literals are enclosed in forward slashes and may contain zero -or more trailing flag characters. Interpretation of escape sequences -within regular expression literals is subject of the underlying -regular expression engine. - --- Expect stdout -- -[ "/Hello world/", "/test/gis", "/test/g", "/test1 \\\/ test2/", "/\\x31\n\\.\u0007\b\\c\\u2600\\\\/" ] --- End -- - --- Testcase -- -{% - print([ - /Hello world/, // A very simple expression - /test/gsi, // Regular expression flags - /test/gg, // Repeated flags - /test1 \/ test2/, // Escaped forward slash - /\x31\n\.\a\b\c\u2600\\/ // Ensure that escape sequences are passed as-is - ], "\n"); -%} --- End -- - - -Testing regular expression type. - --- Expect stdout -- -object --- End -- - --- Testcase -- -{{ type(/foo/) }} --- End -- - - -Testing invalid flag characters. - --- Expect stderr -- -Syntax error: Unexpected token -Expecting ';' -In line 2, byte 8: - - ` /test/x` - ^-- Near here - - --- End -- - --- Testcase -- -{% - /test/x -%} --- End -- - - -Testing unclosed regular expression. - --- Expect stderr -- -Syntax error: Unterminated string -In line 2, byte 2: - - ` /foo \/` - ^-- Near here - - --- End -- - --- Testcase -- -{% - /foo \/ -%} --- End -- - - -Testing regex compilation errors. - --- Expect stderr -- -Syntax error: Unmatched \{ -In line 2, byte 3: - - ` /foo {/` - ^-- Near here - - --- End -- - --- Testcase -- -{% - /foo {/ -%} --- End -- diff --git a/tests/01_arithmetic/00_value_conversion b/tests/01_arithmetic/00_value_conversion deleted file mode 100644 index c44ad00..0000000 --- a/tests/01_arithmetic/00_value_conversion +++ /dev/null @@ -1,125 +0,0 @@ -Performing unary plus or minus, or performing arithmetic operations may -implicitly convert non-numeric values to numeric ones. - -If an addition is performed and either operand is a string, the other -operand is converted into a string as well and the addition result is a -concatenated string consisting of the two operand values. - --- Expect stdout -- -HelloWorld -12 -34 -true -false -null -{ "some": "object" } -[ "some", "array" ] -function() { ... } -1.2 -Infinity -124 -123 -123 -NaN -NaN -NaN -124.2 -Infinity -1 -0 -0 -NaN -NaN -NaN -1.2 -Infinity -123 -12.3 -NaN --1 -0 -0 -NaN -NaN -NaN --1.2 --Infinity --123 --12.3 -NaN -4.2 -9.6 --- End -- - --- Testcase -- -{% - print(join("\n", [ - - // Adding two strings concatenates them: - "Hello" + "World", - - // Adding a number to a string results in a string: - "1" + 2, - - // Adding a string to a number results in a string: - 3 + "4", - - // Adding any non-string value to a string or vice versa will - // force stringification of the non-string value - "" + true, - "" + false, - "" + null, - "" + { some: "object" }, - "" + [ "some", "array" ], - "" + function() {}, - "" + 1.2, - "" + (1 / 0), - - // Adding a numeric value to a non-string, non-numeric value - // or vice versa will convert the non-numeric argument to a - // number - 123 + true, - 123 + false, - 123 + null, - 123 + { some: "object" }, - 123 + [ "some", "array" ], - 123 + function() {}, - 123 + 1.2, - 123 + (1 / 0), - - // The unary "+" operator follows the same logic as adding a - // non-numeric, non-string value to a numeric one. Additionally - // the unary plus forces conversion of string values into numbers - +true, - +false, - +null, - +{ some: "object" }, - +[ "some", "array" ], - +function() {}, - +1.2, - +(1 / 0), - +"123", - +"12.3", - +"this is not a number", - - // The unary "-" operator functions like the unary "+" one and - // it additionally returns the negation of the numeric value - -true, - -false, - -null, - -{ some: "object" }, - -[ "some", "array" ], - -function() {}, - -1.2, - -(1 / 0), - -"123", - -"12.3", - -"this is not a number", - - // Adding a double to an integer or vice versa will force the - // result to a double as well - 1.2 + 3, - 4 + 5.6, - - "" ])); --- End -- diff --git a/tests/01_arithmetic/01_division b/tests/01_arithmetic/01_division deleted file mode 100644 index d4a2adb..0000000 --- a/tests/01_arithmetic/01_division +++ /dev/null @@ -1,53 +0,0 @@ -While arithmetic divisions generally follow the value conversion rules -outlined in the "00_value_conversion" test case, a number of additional -constraints apply. - --- Expect stdout -- -Division by zero yields Infinity: -1 / 0 = Infinity - -Division by Infinity yields zero: -1 / Infinity = 0 - -Dividing Infinity yields Infinity: -Infinity / 1 = Infinity - -Dividing Infinity by Infinity yields NaN: -Infinity / Infinity = NaN - -If either operand is NaN, the result is NaN: -1 / NaN = NaN -NaN / 1 = NaN - -If both operands are integers, integer division is performed: -10 / 3 = 3 - -If either operand is a double, double division is performed: -10.0 / 3 = 3.33333 -10 / 3.0 = 3.33333 --- End -- - --- Testcase -- -Division by zero yields Infinity: -1 / 0 = {{ 1 / 0 }} - -Division by Infinity yields zero: -1 / Infinity = {{ 1 / Infinity }} - -Dividing Infinity yields Infinity: -Infinity / 1 = {{ Infinity / 1 }} - -Dividing Infinity by Infinity yields NaN: -Infinity / Infinity = {{ Infinity / Infinity }} - -If either operand is NaN, the result is NaN: -1 / NaN = {{ 1 / NaN }} -NaN / 1 = {{ NaN / 1 }} - -If both operands are integers, integer division is performed: -10 / 3 = {{ 10 / 3 }} - -If either operand is a double, double division is performed: -10.0 / 3 = {{ 10.0 / 3 }} -10 / 3.0 = {{ 10 / 3.0 }} --- End -- diff --git a/tests/01_arithmetic/02_modulo b/tests/01_arithmetic/02_modulo deleted file mode 100644 index 244d624..0000000 --- a/tests/01_arithmetic/02_modulo +++ /dev/null @@ -1,32 +0,0 @@ -The utpl language supports modulo divisions, however they're only defined -for integer values. - --- Expect stdout -- -If both operands are integers or convertible to integers, -the modulo division yields the remainder: -10 % 4 = 2 -"10" % 4 = 2 -10 % "4" = 2 -"10" % "4" = 2 - -If either operand is a double value, the modulo operation -yields NaN: -10.0 % 4 = NaN -10 % 4.0 = NaN -"10.0" % 4 = NaN --- End -- - --- Testcase -- -If both operands are integers or convertible to integers, -the modulo division yields the remainder: -10 % 4 = {{ 10 % 4 }} -"10" % 4 = {{ "10" % 4 }} -10 % "4" = {{ 10 % 4 }} -"10" % "4" = {{ "10" % "4" }} - -If either operand is a double value, the modulo operation -yields NaN: -10.0 % 4 = {{ 10.0 % 4 }} -10 % 4.0 = {{ 10 % 4.0 }} -"10.0" % 4 = {{ "10.0" % 4 }} --- End -- diff --git a/tests/01_arithmetic/03_bitwise b/tests/01_arithmetic/03_bitwise deleted file mode 100644 index faf4ffd..0000000 --- a/tests/01_arithmetic/03_bitwise +++ /dev/null @@ -1,54 +0,0 @@ -Utpl implements C-style bitwise operations. One detail is that these operations -coerce their operands to signed 64bit integer values internally. - --- Expect stdout -- -Left shift: -10 << 2 = 40 -3.3 << 4.1 = 48 - -Right shift: -10 >> 2 = 2 -3.3 >> 4.1 = 0 - -Bitwise and: -1234 & 200 = 192 -120.3 & 54.3 = 48 - -Bitwise xor: -1234 ^ 200 = 1050 -120.3 ^ 54.3 = 78 - -Bitwise or: -1234 | 200 = 1242 -120.3 | 54.3 = 126 - -Complement: -~0 = -1 -~10.4 = -11 --- End -- - --- Testcase -- -Left shift: -10 << 2 = {{ 10 << 2 }} -3.3 << 4.1 = {{ 3.3 << 4.1 }} - -Right shift: -10 >> 2 = {{ 10 >> 2 }} -3.3 >> 4.1 = {{ 3.3 >> 4.1 }} - -Bitwise and: -1234 & 200 = {{ 1234 & 200 }} -120.3 & 54.3 = {{ 120.3 & 54.3 }} - -Bitwise xor: -1234 ^ 200 = {{ 1234 ^ 200 }} -120.3 ^ 54.3 = {{ 120.3 ^ 54.3 }} - -Bitwise or: -1234 | 200 = {{ 1234 | 200 }} -120.3 | 54.3 = {{ 120.3 | 54.3 }} - -Complement: -~0 = {{ ~0 }} -~10.4 = {{ ~10.4 }} --- End -- diff --git a/tests/01_arithmetic/04_inc_dec b/tests/01_arithmetic/04_inc_dec deleted file mode 100644 index ae50ceb..0000000 --- a/tests/01_arithmetic/04_inc_dec +++ /dev/null @@ -1,49 +0,0 @@ -Utpl implements C-style pre- and postfix increment and decrement operators. - -Pre-increment or -decrement operations first mutate the value and then return -the resulting value while post-increment or -decrement operations first return -the initial value and then mutate the operand. - -Since the decrement and increment operators mutate their operand, they -may only be applied to variables, not constant literal expressions. - -If an undefined variable is incremented or decremented, its initial value -is assumed to be "0". - -If a non-numeric value is incremented or decremented, it is converted to a -number first. If the value is not convertible, the result of the increment -or decrement operation is NaN. - --- Expect stdout -- -Incrementing a not existing variable assumes "0" as initial value: - - - Postfix increment result: 0, value after: 1 - - Prefix increment result: 1, value after: 1 - - Postfix decrement result: 0, value after: -1 - - Prefix decrement result: -1, value after: -1 - -Incrementing a non-numeric value will convert it to a number: - -124 -3.5 -2 -NaN --- End -- - --- Testcase -- -Incrementing a not existing variable assumes "0" as initial value: - - - Postfix increment result: {{ "" + a++ }}, value after: {{ a }} - - Prefix increment result: {{ "" + ++b }}, value after: {{ b }} - - Postfix decrement result: {{ "" + c-- }}, value after: {{ c }} - - Prefix decrement result: {{ "" + --d }}, value after: {{ d }} - -Incrementing a non-numeric value will convert it to a number: - -{% - n = "123"; n++; print(n, "\n"); - n = "4.5"; n--; print(n, "\n"); - n = true; n++; print(n, "\n"); - n = { some: "object" }; n--; print(n, "\n"); -%} --- End -- diff --git a/tests/02_runtime/00_scoping b/tests/02_runtime/00_scoping deleted file mode 100644 index 5fadf43..0000000 --- a/tests/02_runtime/00_scoping +++ /dev/null @@ -1,161 +0,0 @@ -Utpl implements function scoping, make sure that let variables are -invisible outside of the function scope. - --- Expect stdout -- -a_global=true -a_local=true - -b_global=true -b_local=false - -c_global=true -c_local=false - - -When seting a nonlocal variable, it is set in the nearest parent -scope containing the variable or in the root scope if the variable -was not found. - -x=2 -y= -z=1 - - -Variables implicitly declared by for-in or counting for loops follow the same -scoping rules. - -inner2 f_a=3 -inner2 f_b= -inner2 f_c=3 -inner2 f_d= -inner2 f_e=3 - -inner f_a=3 -inner f_b= -inner f_c=3 -inner f_d= -inner f_e=3 - -outer f_a=3 -outer f_b= -outer f_c= -outer f_d= -outer f_e=3 --- End -- - --- Testcase -- -{% - a_global = true; - let a_local = true; - - function test() { - b_global = true; - let b_local = true; - - function test2() { - c_global = true; - let c_local = true; - } - - test2(); - } - - test(); --%} - -a_global={{ !!a_global }} -a_local={{ !!a_local }} - -b_global={{ !!b_global }} -b_local={{ !!b_local }} - -c_global={{ !!c_global }} -c_local={{ !!c_local }} - - -When seting a nonlocal variable, it is set in the nearest parent -scope containing the variable or in the root scope if the variable -was not found. - -{% - x = 1; - - function scope1() { - x = 2; - let y; - - function scope2() { - // this does not set "y" in the root scope but overwrites the - // variable declared in the "scope1" function scope. - y = 2; - - // this sets "z" in the root scope because it was not declared - // anywhere yet - z = 1; - } - - scope2(); - } - - scope1(); --%} - -x={{ x }} -y={{ y }} -z={{ z }} - - -Variables implicitly declared by for-in or counting for loops follow the same -scoping rules. - -{% - function scope3() { - // f_a is not declared local and be set in the root scope - for (f_a = 1; f_a < 3; f_a++) - ; - - for (let f_b = 1; f_b < 3; f_b++) - ; - - let f_c; - - function scope4() { - // f_c is not declared local but declared in the parent scope, it - // will be set there - for (f_c in [1, 2, 3]) - ; - - for (let f_d in [1, 2, 3]) - ; - - // f_e is not declared, it will be set in the root scope - for (f_e in [1, 2, 3]) - ; - - print("inner2 f_a=", f_a, "\n"); - print("inner2 f_b=", f_b, "\n"); - print("inner2 f_c=", f_c, "\n"); - print("inner2 f_d=", f_d, "\n"); - print("inner2 f_e=", f_e, "\n"); - print("\n"); - } - - scope4(); - - print("inner f_a=", f_a, "\n"); - print("inner f_b=", f_b, "\n"); - print("inner f_c=", f_c, "\n"); - print("inner f_d=", f_d, "\n"); - print("inner f_e=", f_e, "\n"); - print("\n"); - } - - scope3(); - - print("outer f_a=", f_a, "\n"); - print("outer f_b=", f_b, "\n"); - print("outer f_c=", f_c, "\n"); - print("outer f_d=", f_d, "\n"); - print("outer f_e=", f_e, "\n"); -%} --- End -- diff --git a/tests/02_runtime/01_break_continue b/tests/02_runtime/01_break_continue deleted file mode 100644 index a27d072..0000000 --- a/tests/02_runtime/01_break_continue +++ /dev/null @@ -1,50 +0,0 @@ -The "break" and "continue" statements allow to abort a running loop or to -prematurely advance to the next cycle. - --- Expect stdout -- -Testing break: - - Iteration 0 - - Iteration 1 - - Iteration 2 - - Iteration 3 - - Iteration 4 - - Iteration 5 - - Iteration 6 - - Iteration 7 - - Iteration 8 - - Iteration 9 - - Iteration 10 - -Testing continue: - - Iteration 0 - - Iteration 2 - - Iteration 4 - - Iteration 6 - - Iteration 8 --- End -- - --- Testcase -- -Testing break: -{% - let i = 0; - - while (true) { - print(" - Iteration ", i, "\n"); - - if (i == 10) - break; - - i++; - } -%} - -Testing continue: -{% - for (i = 0; i < 10; i++) { - if (i % 2) - continue; - - print(" - Iteration ", i, "\n"); - } -%} --- End -- diff --git a/tests/02_runtime/02_this b/tests/02_runtime/02_this deleted file mode 100644 index d8e85d2..0000000 --- a/tests/02_runtime/02_this +++ /dev/null @@ -1,50 +0,0 @@ -The "this" object accesses the current function context. - --- Expect stdout -- -true -true --- End -- - --- Testcase -- -{% - // Functions not invoked on objects have no this context - function test() { - return (this === null); - } - - // When invoked, "this" will point to the object containing the function - let o; - o = { - test: function() { - return (this === o); - } - }; - - print(test(), "\n"); - print(o.test(), "\n"); -%} --- End -- - -Test that the context is properly restored if function call arguments are -dot or bracket expressions as well. - --- Expect stdout -- -true -true --- End -- - --- Testcase -- -{% - let o; - o = { - test: function() { - return (this === o); - } - }; - - let dummy = { foo: true, bar: false }; - - print(o.test(dummy.foo, dummy.bar), "\n"); - print(o.test(dummy.foo, o.test(dummy.foo, dummy.bar)), "\n"); -%} --- End -- diff --git a/tests/02_runtime/03_try_catch b/tests/02_runtime/03_try_catch deleted file mode 100644 index 751ca1d..0000000 --- a/tests/02_runtime/03_try_catch +++ /dev/null @@ -1,138 +0,0 @@ -Wrapping an exeptional operation in try {} catch {} allows handling the -resulting exception and to continue the execution flow. - --- Expect stdout -- -Catched first exception. -Catched second exception: exception 2. -After exceptions. --- End -- - --- Testcase -- -{% - // A try-catch block that discards the exception information. - try { - die("exception 1"); - } - catch { - print("Catched first exception.\n"); - } - - // A try-catch block that captures the resulting exception in - // the given variable. - try { - die("exception 2"); - } - catch (e) { - print("Catched second exception: ", e, ".\n"); - } - - print("After exceptions.\n"); -%} --- End -- - - -Ensure that exceptions are propagated through C function calls. - --- Expect stderr -- -exception -In [anonymous function](), line 3, byte 18: - called from function replace ([C]) - called from anonymous function ([stdin]:4:3) - - ` die("exception");` - Near here -------------^ - - --- End -- - --- Testcase -- -{% - replace("test", "t", function(m) { - die("exception"); - }); -%} --- End -- - - -Ensure that exception can be catched through C function calls. - --- Expect stdout -- -Caught exception: exception --- End -- - --- Testcase -- -{% - try { - replace("test", "t", function(m) { - die("exception"); - }); - } - catch (e) { - print("Caught exception: ", e, "\n"); - } -%} --- End -- - - -Ensure that exceptions are propagated through user function calls. - --- Expect stderr -- -exception -In a(), line 3, byte 18: - called from function b ([stdin]:7:5) - called from function c ([stdin]:11:5) - called from anonymous function ([stdin]:14:4) - - ` die("exception");` - Near here -------------^ - - --- End -- - --- Testcase -- -{% - function a() { - die("exception"); - } - - function b() { - a(); - } - - function c() { - b(); - } - - c(); -%} --- End -- - - -Ensure that exceptions can be caught in parent functions. - --- Expect stdout -- -Caught exception: exception --- End -- - --- Testcase -- -{% - function a() { - die("exception"); - } - - function b() { - a(); - } - - function c() { - try { - b(); - } - catch (e) { - print("Caught exception: ", e, "\n"); - } - } - - c(); -%} --- End -- diff --git a/tests/02_runtime/04_switch_case b/tests/02_runtime/04_switch_case deleted file mode 100644 index 4c1fc57..0000000 --- a/tests/02_runtime/04_switch_case +++ /dev/null @@ -1,325 +0,0 @@ -Testing utpl switch statements. - - -1. Ensure that execution starts at the first matching case. - --- Expect stdout -- -1a --- End -- - --- Testcase -- -{% - switch (1) { - case 1: - print("1a\n"); - break; - - case 1: - print("1b\n"); - break; - - case 2: - print("2\n"); - break; - } -%} --- End -- - - -2. Ensure that default case is only used if no case matches, - even if declared first. - --- Expect stdout -- -1 -default --- End -- - --- Testcase -- -{% - for (n in [1, 3]) { - switch (n) { - default: - print("default\n"); - break; - - case 1: - print("1\n"); - break; - - case 2: - print("2\n"); - break; - } - } -%} --- End -- - - -3. Ensure that cases without break fall through into - subsequent cases. - --- Expect stdout -- -1 -2 -default -1 -2 --- End -- - --- Testcase -- -{% - for (n in [1, 3]) { - switch (n) { - default: - print("default\n"); - - case 1: - print("1\n"); - - case 2: - print("2\n"); - } - } -%} --- End -- - - -4. Ensure that a single default case matches. - --- Expect stdout -- -default -default --- End -- - --- Testcase -- -{% - for (n in [1, 3]) { - switch (n) { - default: - print("default\n"); - } - } -%} --- End -- - - -5. Ensure that duplicate default cases emit a syntax - error during parsing. - --- Expect stderr -- -Syntax error: more than one switch default case -In line 6, byte 3: - - ` default:` - ^-- Near here - - -Syntax error: Expecting expression -In line 8, byte 2: - - ` }` - ^-- Near here - - --- End -- - --- Testcase -- -{% - switch (1) { - default: - print("default1\n"); - - default: - print("default2\n"); - } -%} --- End -- - - -6. Ensure that case values use strict comparison. - --- Expect stdout -- -b -b --- End -- - --- Testcase -- -{% - switch (1.0) { - case 1: - print("a\n"); - break; - - case 1.0: - print("b\n"); - break; - } - - switch ("123") { - case 123: - print("a\n"); - break; - - case "123": - print("b\n"); - break; - } -%} --- End -- - - -7. Ensure that case values may be complex expressions. - --- Expect stdout -- -2, 3, 1 --- End -- - --- Testcase -- -{% - switch (1) { - case a = 2, b = 3, c = 1: - print(join(", ", [ a, b, c ]), "\n"); - break; - } -%} --- End -- - - -8. Ensure that empty switch statements are accepted by the - parser and that the test expression is evaluated. - --- Expect stdout -- -true --- End -- - --- Testcase -- -{% - x = false; - - switch (x = true) { - - } - - print(x, "\n"); -%} --- End -- - - -9. Ensure that `return` breaks out of switch statements. - --- Expect stdout -- -one -two --- End -- - --- Testcase -- -{% - function test(n) { - switch (n) { - case 1: - return "one"; - - case 2: - return "two"; - - default: - return "three"; - } - } - - print(test(1), "\n"); - print(test(2), "\n"); -%} --- End -- - - -10. Ensure that `continue` breaks out of switch statements. - --- Expect stdout -- -one -two --- End -- - --- Testcase -- -{% - for (n in [1,2]) { - switch (n) { - case 1: - print("one\n"); - continue; - - case 2: - print("two\n"); - continue; - - default: - print("three\n"); - } - } -%} --- End -- - - -11. Ensure that exceptions break out of switch statements. - --- Expect stdout -- -one --- End -- - --- Expect stderr -- -Died -In test(), line 6, byte 8: - called from anonymous function ([stdin]:17:14) - - ` die();` - Near here ------^ - - --- End -- - --- Testcase -- -{% - function test(n) { - switch (n) { - case 1: - print("one\n"); - die(); - - case 2: - print("two\n"); - die(); - - default: - print("three\n"); - } - } - - print(test(1), "\n"); -%} --- End -- - - -12. Ensure that consecutive cases values are properly handled. - --- Expect stdout -- -three and four --- End -- - --- Testcase -- -{% - switch (3) { - case 1: - case 2: - print("one and two\n"); - break; - - case 3: - case 4: - print("three and four\n"); - break; - - default: - print("five\n"); - } -%} --- End -- diff --git a/tests/02_runtime/05_closure_scope b/tests/02_runtime/05_closure_scope deleted file mode 100644 index c59a433..0000000 --- a/tests/02_runtime/05_closure_scope +++ /dev/null @@ -1,35 +0,0 @@ -Testing closure scopes. - - -1. Ensure that the declaring scope is retained in functions. - --- Expect stdout -- -Make function with x=1 -Make function with x=2 -Make function with x=3 -x is 1 -x is 2 -x is 3 --- End -- - --- Testcase -- -{% - let count=0; - - function a() { - let x = ++count; - print("Make function with x=", x, "\n"); - return function() { - print("x is ", x, "\n"); - }; - } - - let fn1 = a(); - let fn2 = a(); - let fn3 = a(); - - fn1(); - fn2(); - fn3(); -%} --- End -- diff --git a/tests/02_runtime/06_recursion b/tests/02_runtime/06_recursion deleted file mode 100644 index 470fc3a..0000000 --- a/tests/02_runtime/06_recursion +++ /dev/null @@ -1,59 +0,0 @@ -Testing recursive invocations. - - -1. Testing recursive invocation. - --- Expect stdout -- -1 -2 -4 -8 -16 -32 -64 -128 -256 -512 -1024 -2048 -4096 -8192 -16384 --- End -- - --- Testcase -- -{% - function test(x) { - print(x, "\n"); - - if (x < 10000) - test(x * 2); - } - - test(1); -%} --- End -- - - -2. Testing infinite recursion. - --- Expect stderr -- -Runtime error: Too much recursion -In test(), line 3, byte 8: - called from anonymous function ([stdin]:6:7) - - ` test();` - Near here ---^ - - --- End -- - --- Testcase -- -{% - function test() { - test(); - } - - test(); -%} --- End -- diff --git a/tests/03_bugs/01_try_catch_stack_mismatch b/tests/03_bugs/01_try_catch_stack_mismatch deleted file mode 100644 index f6e5a0a..0000000 --- a/tests/03_bugs/01_try_catch_stack_mismatch +++ /dev/null @@ -1,52 +0,0 @@ -When compiling a try/catch statement with an exception variable, the catch -skip jump incorrectly pointed to the POP instruction popping the exception -variable off the stack, leading to a stack position mismatch between -compiler and vm, causing local variables to yield wrong values at runtime. - --- Expect stdout -- -1 --- End -- - --- Testcase -- -{% - function f() { - let x; - - try { - x = 1; - } - catch(e) { - - } - - // Before the fix, `x` incorrectly yielded the print function value - print(x, "\n"); - } - - f() -%} --- End -- - - -When compiling a try/catch statement with local variable declearations -within the try block, the catch skip jump incorrectly happened before the -local try block variables were popped off the stack, leading to a stack -position mismatch between compiler and vm, causing local variables to -yield wrong values at runtime. - --- Expect stdout -- -1 --- End -- - --- Testcase -- -{% - try { - let a; - } - catch {} - - let b = 1; - - print(b, "\n"); -%} --- End -- diff --git a/tests/03_bugs/02_array_pop_use_after_free b/tests/03_bugs/02_array_pop_use_after_free deleted file mode 100644 index 22f63ff..0000000 --- a/tests/03_bugs/02_array_pop_use_after_free +++ /dev/null @@ -1,14 +0,0 @@ -When popping an element off an array, especially the last one, the popped -value might have been freed before the refcount was increased later on -function return. - --- Expect stdout -- -1 --- End -- - --- Testcase -- -{% - x = [1]; - print(pop(x), "\n"); // This caused a SIGABRT before the bugfix -%} --- End -- diff --git a/tests/03_bugs/03_switch_fallthrough_miscompilation b/tests/03_bugs/03_switch_fallthrough_miscompilation deleted file mode 100644 index 3e6410e..0000000 --- a/tests/03_bugs/03_switch_fallthrough_miscompilation +++ /dev/null @@ -1,16 +0,0 @@ -When falling through from a matched switch case into the default case, -the compiler incorrectly emitted bytecode that led to an endless loop. - --- Expect stdout -- -1 --- End -- - --- Testcase -- -{% - switch (1) { - case 1: - default: - print("1\n"); - } -%} --- End -- diff --git a/tests/03_bugs/04_property_set_abort b/tests/03_bugs/04_property_set_abort deleted file mode 100644 index 8af477f..0000000 --- a/tests/03_bugs/04_property_set_abort +++ /dev/null @@ -1,76 +0,0 @@ -When attempting to set a property on a non-array, non-object value the -VM aborted due to an assert triggered by libjson-c. - --- Testcase -- -{% (null).x = 1 %} --- End -- - --- Expect stderr -- -Type error: attempt to set property on null value -In line 1, byte 15: - - `{% (null).x = 1 %}` - Near here ----^ - - --- End -- - - --- Testcase -- -{% (1).x = 1 %} --- End -- - --- Expect stderr -- -Type error: attempt to set property on integer value -In line 1, byte 12: - - `{% (1).x = 1 %}` - Near here -^ - - --- End -- - - --- Testcase -- -{% (1.2).x = 1 %} --- End -- - --- Expect stderr -- -Type error: attempt to set property on double value -In line 1, byte 14: - - `{% (1.2).x = 1 %}` - Near here ---^ - - --- End -- - - --- Testcase -- -{% (true).x = 1 %} --- End -- - --- Expect stderr -- -Type error: attempt to set property on boolean value -In line 1, byte 15: - - `{% (true).x = 1 %}` - Near here ----^ - - --- End -- - - --- Testcase -- -{% ("test").x = 1 %} --- End -- - --- Expect stderr -- -Type error: attempt to set property on string value -In line 1, byte 17: - - `{% ("test").x = 1 %}` - Near here ------^ - - --- End -- diff --git a/tests/03_bugs/05_duplicate_ressource_type b/tests/03_bugs/05_duplicate_ressource_type deleted file mode 100644 index 21166b2..0000000 --- a/tests/03_bugs/05_duplicate_ressource_type +++ /dev/null @@ -1,31 +0,0 @@ -When requiring a C module that registers custom ressource types multiple -times, ressource values instantiated after subsequent requires of the -same extensions didn't properly function since the internal type prototype -was resolved to the initial copy and subsequently discarded due to an index -mismatch. - --- Testcase -- -{% - fs = require("fs"); - fd = fs.open("README.md"); - - printf("fd.read() #1: %s\n", - fd.read("line") ? "working" : "not working (" + fd.error() + ")"); - - fd.close(); - - - fs = require("fs"); - fd = fs.open("README.md"); - - printf("fd.read() #2: %s\n", - fd.read("line") ? "working" : "not working (" + fd.error() + ")"); - - fd.close(); -%} --- End -- - --- Expect stdout -- -fd.read() #1: working -fd.read() #2: working --- End -- diff --git a/tests/03_bugs/06_lexer_escape_at_boundary b/tests/03_bugs/06_lexer_escape_at_boundary deleted file mode 100644 index e80b0a1..0000000 --- a/tests/03_bugs/06_lexer_escape_at_boundary +++ /dev/null @@ -1,12 +0,0 @@ -When the lexer processed a backslash introducing a string escape directly -at the buffer boundary, the backslash was incorrectly retained. - --- Testcase -- -{% - print("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl\n"); -%} --- End -- - --- Expect stdout -- -abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl --- End -- diff --git a/tests/03_bugs/07_lexer_overlong_lines b/tests/03_bugs/07_lexer_overlong_lines deleted file mode 100644 index d2dd3be..0000000 --- a/tests/03_bugs/07_lexer_overlong_lines +++ /dev/null @@ -1,13 +0,0 @@ -A logic flaw in the lineinfo encoding function led to an endless tight -loop when a buffer chunk with 128 byte got consumed, which may happen -when parsing very long literals. - --- Testcase -- -{% - print("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg\n"); -%} --- End -- - --- Expect stdout -- -abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg --- End -- diff --git a/tests/03_bugs/08_compiler_arrow_fn_expressions b/tests/03_bugs/08_compiler_arrow_fn_expressions deleted file mode 100644 index 5cd8960..0000000 --- a/tests/03_bugs/08_compiler_arrow_fn_expressions +++ /dev/null @@ -1,15 +0,0 @@ -Arrow functions with single expression bodies were parsed with a wrong -precedence level, causing comma expressions to be greedily consumed. - --- Testcase -- -{% - print({ - a: () => 1, - b: () => 2 - }, "\n"); -%} --- End -- - --- Expect stdout -- -{ "a": "() => { ... }", "b": "() => { ... }" } --- End -- diff --git a/tests/03_bugs/09_reject_invalid_array_indexes b/tests/03_bugs/09_reject_invalid_array_indexes deleted file mode 100644 index a7e5272..0000000 --- a/tests/03_bugs/09_reject_invalid_array_indexes +++ /dev/null @@ -1,25 +0,0 @@ -Since libjson-c's json_object_get_int64() returns 0 for any input value -that has no integer representation, any kind of invalid array index -incorrectly yielded the first array element. - --- Testcase -- -{% - x = [1, 2, 3]; - - print([ - x[1], - x["1"], - x[1.0], - x[1.1], - x.foo, - x["foo"], - x["0abc"], - x[x], - x[{ foo: true }] - ], "\n"); -%} --- End -- - --- Expect stdout -- -[ 2, 2, 2, null, null, null, null, null, null ] --- End -- diff --git a/tests/03_bugs/10_break_stack_mismatch b/tests/03_bugs/10_break_stack_mismatch deleted file mode 100644 index ae16dac..0000000 --- a/tests/03_bugs/10_break_stack_mismatch +++ /dev/null @@ -1,38 +0,0 @@ -When emitting jump instructions for breaking out of for-loops, the compiler -incorrectly set the jump target before the pop instruction clearing the -intermediate loop variables. Since the break instruction itself already -compiles to a series of pop instructions reverting the stack to it's the -pre-loop state, intermediate values got popped twice, leading to a stack -layout mismatch between compiler and VM, resulting in wrong local variable -values or segmentation faults at runtime. - --- Testcase -- -{% - let x = 1; - - for (let y in [2]) - break; - - print(x, "\n"); -%} --- End -- - --- Expect stdout -- -1 --- End -- - - --- Testcase -- -{% - let x = 1; - - for (let y = 0; y < 1; y++) - break; - - print(x, "\n"); -%} --- End -- - --- Expect stdout -- -1 --- End -- diff --git a/tests/03_bugs/11_switch_stack_mismatch b/tests/03_bugs/11_switch_stack_mismatch deleted file mode 100644 index cc3b41a..0000000 --- a/tests/03_bugs/11_switch_stack_mismatch +++ /dev/null @@ -1,39 +0,0 @@ -When jumping into a case following prior cases declaring local variables, -the preceding local variable declarations were skipped, leading to an -unexpected stack layout which caused local variables to carry wrong -values at run time and eventual segmentation faults when attempting to -unwind the stack on leaving the lexical switch scope. - --- Expect stdout -- -Matching 1: - - 1: [ null, null, 3, 4 ] - - 2: [ null, null, 3, 4, 5, 6 ] -Matching 2: - - 2: [ null, null, null, null, 5, 6 ] -Matching 3: - - default: [ 1, 2 ] - - 1: [ 1, 2, 3, 4 ] - - 2: [ 1, 2, 3, 4, 5, 6 ] --- End -- - --- Testcase -- -{% - for (let n in [1, 2, 3]) { - printf("Matching %d:\n", n); - - switch (n) { - default: - let x = 1, y = 2; - print(" - default: ", [x, y], "\n"); - - case 1: - let a = 3, b = 4; - print(" - 1: ", [x, y, a, b], "\n"); - - case 2: - let c = 5, d = 6; - print(" - 2: ", [x, y, a, b, c, d], "\n"); - } - } -%} --- End -- diff --git a/tests/03_bugs/12_altblock_stack_mismatch b/tests/03_bugs/12_altblock_stack_mismatch deleted file mode 100644 index e350660..0000000 --- a/tests/03_bugs/12_altblock_stack_mismatch +++ /dev/null @@ -1,83 +0,0 @@ -When compiling alternative syntax blocks, such as `for ...: endfor`, -`if ...: endif` etc., the compiler didn't assign the contained statements -to a dedicated lexical scope, which caused a stack mismatch between -compiler and vm when such blocks declaring local variables weren't -actually executed. - --- Expect stdout -- -2 --- End -- - --- Testcase -- -{% - if (false): - let a = 1; - endif; - - /* Due to lack of own lexical scope above, the compiler assumed - * that `a` is still on stack but the code to initialize it was - * never executed, so stack offsets were shifted by one from here - * on throughout the rest of the program. */ - - let b = 2; - - print(b, "\n"); -%} --- End -- - - -Test a variation of the bug using `for in..endfor` loop syntax. - --- Expect stdout -- -2 --- End -- - --- Testcase -- -{% - for (let x in []): - let a = 1; - endfor; - - let b = 2; - - print(b, "\n"); -%} --- End -- - - -Test a variation of the bug using `for..endfor` count loop syntax. - --- Expect stdout -- -2 --- End -- - --- Testcase -- -{% - for (let i = 0; i < 0; i++): - let a = 1; - endfor; - - let b = 2; - - print(b, "\n"); -%} --- End -- - - -Test a variation of the bug using `while..endwhile` loop syntax. - --- Expect stdout -- -2 --- End -- - --- Testcase -- -{% - while (false): - let a = 1; - endwhile; - - let b = 2; - - print(b, "\n"); -%} --- End -- diff --git a/tests/custom/00_syntax/00_single_line_comments b/tests/custom/00_syntax/00_single_line_comments new file mode 100644 index 0000000..24a32a2 --- /dev/null +++ b/tests/custom/00_syntax/00_single_line_comments @@ -0,0 +1,15 @@ +Single line comments. + +-- Expect stdout -- +This is a test. + a test. +A test . + +-- End -- + +-- Testcase -- +This is {# a comment within #} a test. +{# Comment before #} a test. +A test {# and a comment after #}. +{# This is a comment with embedded "{#" tag. #} +-- End -- diff --git a/tests/custom/00_syntax/01_unterminated_comment b/tests/custom/00_syntax/01_unterminated_comment new file mode 100644 index 0000000..1d3669c --- /dev/null +++ b/tests/custom/00_syntax/01_unterminated_comment @@ -0,0 +1,15 @@ +Unterminated comment + +-- Expect stderr -- +Syntax error: Unterminated template block +In line 1, byte 9: + + `This is {# an unclosed comment` + ^-- Near here + + +-- End -- + +-- Testcase -- +This is {# an unclosed comment +-- End -- diff --git a/tests/custom/00_syntax/02_multi_line_comments b/tests/custom/00_syntax/02_multi_line_comments new file mode 100644 index 0000000..99fc37e --- /dev/null +++ b/tests/custom/00_syntax/02_multi_line_comments @@ -0,0 +1,12 @@ +Multiline comments. + +-- Expect stdout -- +This is an example text for testing comment blocks. +-- End -- + +-- Testcase -- +This is an example text {# containing +a comment spanning multiple lines and + different indentation + #} for testing comment blocks. +-- End -- diff --git a/tests/custom/00_syntax/03_expression_blocks b/tests/custom/00_syntax/03_expression_blocks new file mode 100644 index 0000000..3568016 --- /dev/null +++ b/tests/custom/00_syntax/03_expression_blocks @@ -0,0 +1,11 @@ +Testing expression blocks. + +-- Expect stdout -- +The result of 3 * 7 is 21. +To escape the start tag, output it as string expression: {{ +-- End -- + +-- Testcase -- +The result of 3 * 7 is {{ 3 * 7 }}. +To escape the start tag, output it as string expression: {{ "{{" }} +-- End -- diff --git a/tests/custom/00_syntax/04_statement_blocks b/tests/custom/00_syntax/04_statement_blocks new file mode 100644 index 0000000..920ed71 --- /dev/null +++ b/tests/custom/00_syntax/04_statement_blocks @@ -0,0 +1,20 @@ +Testing statement blocks. + +-- Expect stdout -- +The result of 3 * 7 is 21. +A statement block may contain multiple statements: Hello World +To escape the start tag, output it as string expression: {% +Alternatively print it: {% +-- End -- + +-- Testcase -- +The result of 3 * 7 is {%+ print(3 * 7) %}. +A statement block may contain multiple statements: {%+ + print("Hello "); + print("World"); +%} + +To escape the start tag, output it as string expression: {{ "{%" }} +Alternatively print it: {%+ print("{%") %} + +-- End -- diff --git a/tests/custom/00_syntax/05_block_nesting b/tests/custom/00_syntax/05_block_nesting new file mode 100644 index 0000000..fcfd7da --- /dev/null +++ b/tests/custom/00_syntax/05_block_nesting @@ -0,0 +1,24 @@ +Nesting blocks into non-comment blocks should fail. + +-- Expect stderr -- +Syntax error: Template blocks may not be nested +In line 2, byte 61: + + `We may not nest statement blocks into expression blocks: {{ {% print(1 + 2) %} }}.` + Near here --------------------------------------------------^ + + +Syntax error: Template blocks may not be nested +In line 3, byte 61: + + `We may not nest expression blocks into statement blocks: {% {{ 1 + 2 }} %}.` + Near here --------------------------------------------------^ + + +-- End -- + +-- Testcase -- +We can nest other block types into comments: {# {% {{ 1 + 2 }} %} #} +We may not nest statement blocks into expression blocks: {{ {% print(1 + 2) %} }}. +We may not nest expression blocks into statement blocks: {% {{ 1 + 2 }} %}. +-- End -- diff --git a/tests/custom/00_syntax/06_open_statement_block b/tests/custom/00_syntax/06_open_statement_block new file mode 100644 index 0000000..9c2d142 --- /dev/null +++ b/tests/custom/00_syntax/06_open_statement_block @@ -0,0 +1,13 @@ +The last statement block of a template may remain open, this is useful for templates +that contain only code. + +-- Expect stdout -- +This template consists entirely of script code! +-- End -- + +-- Testcase -- +{% + print("This template "); + print("consists entirely "); + print("of script code!\n"); +-- End -- diff --git a/tests/custom/00_syntax/07_embedded_single_line_comments b/tests/custom/00_syntax/07_embedded_single_line_comments new file mode 100644 index 0000000..43f188c --- /dev/null +++ b/tests/custom/00_syntax/07_embedded_single_line_comments @@ -0,0 +1,21 @@ +Statement and expression blocks may contain C++-style comments. +A C++-style comment is started by two subsequent slashes and spans +until the next newline. + +-- Expect stdout -- +The result of 5 + 9 is 14. +Statement blocks may use C++ comments too: Test Another test. +-- End -- + +-- Testcase -- +The result of 5 + 9 is {{ // The block end tag is ignored: }} +// And the expression block continues here! +5 + 9 }}. +Statement blocks may use C++ comments too: {%+ + print("Test"); // A comment. + + // Another comment. + print(" Another test."); +%} + +-- End -- diff --git a/tests/custom/00_syntax/08_embedded_multi_line_comments b/tests/custom/00_syntax/08_embedded_multi_line_comments new file mode 100644 index 0000000..75aba5f --- /dev/null +++ b/tests/custom/00_syntax/08_embedded_multi_line_comments @@ -0,0 +1,24 @@ +Statement and expression blocks may contain C-style comments. +A C-style comment is started by a slash followed by an asterisk +and ended by an asterisk followed by a slash. + +Such comments may appear everywhere within statement or expression +blocks, even in the middle of statements or expressions. + +-- Expect stdout -- +The result of 12 - 4 is 8. +Statement blocks may use C comments too: Test Another test. The final test. +-- End -- + +-- Testcase -- +The result of 12 - 4 is {{ /* A comment before */ 12 - /* or even within */ 4 /* or after an expression */ }}. +Statement blocks may use C comments too: {%+ + print("Test"); /* A comment. */ + + /* Another comment. */ + print(" Another test."); + + print(/* A comment within */ " The final test."); +%} + +-- End -- diff --git a/tests/custom/00_syntax/09_string_literals b/tests/custom/00_syntax/09_string_literals new file mode 100644 index 0000000..0967850 --- /dev/null +++ b/tests/custom/00_syntax/09_string_literals @@ -0,0 +1,50 @@ +String literals may be enclosed in single or double quotes. +Embedded escape sequences are started with a backslash, followed +by either a hexadecimal, an octal or a single character escape sequence. + +-- Expect stdout -- +Single quoted string +Double quoted string +Unicode escape sequence: ☀💩 +Escaped double quote (") character +Escaped single quote (') character +Hexadecimal escape: XYZ xyz +Octal escape: ABC xyz +{ "Single char escape": "\u0007\b\u001b\f\r\t\u000b\\\n" } +-- End -- + +-- Testcase -- +{{ 'Single quoted string' }} +{{ "Double quoted string" }} +{{ "Unicode escape sequence: \u2600\uD83D\uDCA9" }} +{{ "Escaped double quote (\") character" }} +{{ 'Escaped single quote (\') character' }} +{{ "Hexadecimal escape: \x58\x59\x5A \x78\x79\x7a" }} +{{ "Octal escape: \101\102\103 \170\171\172" }} +{{ { "Single char escape": "\a\b\e\f\r\t\v\\\n" } }} +-- End -- + + +Testing various parsing corner cases. + +-- Expect stdout -- +[ "\t", "\n", "y", "\u0001", "\n", "\u0001\u0002", "\u0001\u0002", "\u0001\u0002", "\u0001a", "\na" ] +-- End -- + +-- Testcase -- +{% + print([ + "\ ", // properly handle escaped tab + "\ +", // properly handle escaped newline + "\y", // substitute unrecognized escape with escaped char + "\1", // handle short octal sequence at end of string + "\12", // handle short octal sequence at end of string + "\1\2", // handle subsequent short octal sequences + "\001\2", // handle short sequence after long one + "\1\002", // handle long sequence after short one + "\1a", // handle short octal sequence terminated by non-octal char + "\12a" // handle short octal sequence terminated by non-octal char + ], "\n"); +%} +-- End -- diff --git a/tests/custom/00_syntax/10_numeric_literals b/tests/custom/00_syntax/10_numeric_literals new file mode 100644 index 0000000..3e367d0 --- /dev/null +++ b/tests/custom/00_syntax/10_numeric_literals @@ -0,0 +1,26 @@ +C-style numeric integer and float literals are understood, as well +as the special keywords "Infinity" and "NaN" to denote the IEEE 754 +floating point values. + +Numeric values are either stored as signed 64 bit integers or signed +doubles internally. + +-- Expect stdout -- +Integers literals: 123, 127, 2748, 57082 +Float literals: 10, 10.3, 1.23456e-65, 16.0625 +Special values: Infinity, Infinity, NaN, NaN +Minimum values: -9223372036854775808, -1.79769e+308 +Maximum values: 9223372036854775807, 1.79769e+308 +Minimum truncation: -9223372036854775808, -Infinity +Maximum truncation: 9223372036854775807, Infinity +-- End -- + +-- Testcase -- +Integers literals: {{ 123 }}, {{ 0177 }}, {{ 0xabc }}, {{ 0xDEFA }} +Float literals: {{ 10. }}, {{ 10.3 }}, {{ 123.456e-67 }}, {{ 0x10.1 }} +Special values: {{ Infinity }}, {{ 1 / 0 }}, {{ NaN }}, {{ "x" / 1 }} +Minimum values: {{ -9223372036854775808 }}, {{ -1.7976931348623158e+308 }} +Maximum values: {{ 9223372036854775807 }}, {{ 1.7976931348623158e+308 }} +Minimum truncation: {{ -10000000000000000000 }}, {{ -1.0e309 }} +Maximum truncation: {{ 10000000000000000000 }}, {{ 1.0e309 }} +-- End -- diff --git a/tests/custom/00_syntax/11_misc_literals b/tests/custom/00_syntax/11_misc_literals new file mode 100644 index 0000000..372741c --- /dev/null +++ b/tests/custom/00_syntax/11_misc_literals @@ -0,0 +1,17 @@ +The utpl script language features a number of keywords which represent +certain special values. + +-- Expect stdout -- +The "this" keyword refers to the current function context: object +The "null" keyword represents the null value: null +The "true" keyword represents a true boolean value: true +The "false" keyword represents a false boolean value: false +-- End -- + +-- Testcase -- +{% let t = { f: function() { return this; } } %} +The "this" keyword refers to the current function context: {{ type(t.f()) }} +The "null" keyword represents the null value: {{ "" + null }} +The "true" keyword represents a true boolean value: {{ true }} +The "false" keyword represents a false boolean value: {{ false }} +-- End -- diff --git a/tests/custom/00_syntax/12_block_whitespace_control b/tests/custom/00_syntax/12_block_whitespace_control new file mode 100644 index 0000000..911171c --- /dev/null +++ b/tests/custom/00_syntax/12_block_whitespace_control @@ -0,0 +1,47 @@ +By default, whitespace before a block start tag or after a block end tag +is retained. By suffixing the start tag or prefixing the end tag with a +dash, the leading or trailing whitespace is trimmed respectively. + +-- Expect stdout -- +Whitespace control applies to all block types: +Comment before: | |, after: | |, both: || +Statement before: |test |, after: | test|, both: |test| +Expression before: |test |, after: | test|, both: |test| + +By default whitespace around a block is retained. +Leading whitespace can be trimmed like this. +The same applies to trailing whitespace. +It is also possible to trim bothleading and trailingwhitespace. + +Stripping works across multiple lines as well:test + +Likewise, stripping over multiple lines of trailing whitespace works as +expected too.This is after the block. +-- End -- + +-- Testcase -- +Whitespace control applies to all block types: +Comment before: | {#- test #} |, after: | {#- test #} |, both: | {#- test -#} | +Statement before: | {%- print("test") %} |, after: | {%+ print("test") -%} |, both: | {%- print("test") -%} | +Expression before: | {{- "test" }} |, after: | {{ "test" -}} |, both: | {{- "test" -}} | + +By default whitespace {{ "around a block" }} is retained. +Leading whitespace can be trimmed {#- note the leading dash #} like this. +The same applies to {# note the trailing dash -#} trailing whitespace. +It is also possible to trim both {{- "leading and trailing" -}} whitespace. + +Stripping works across multiple lines as well: + +{%- + /* The word "test" will be printed after "well:" above */ + print("test") +%} + + +Likewise, stripping over multiple lines of trailing whitespace works as +expected too. + +{#- Any whitespace after "expected too." and before "This is after the block" will be trimmed. -#} + +This is after the block. +-- End -- diff --git a/tests/custom/00_syntax/13_object_literals b/tests/custom/00_syntax/13_object_literals new file mode 100644 index 0000000..18fbbed --- /dev/null +++ b/tests/custom/00_syntax/13_object_literals @@ -0,0 +1,174 @@ +The utpl script language supports declaring objects (dictionaries) using +either JSON or JavaScript notation. + +-- Expect stdout -- +{ } +{ "name": "Bob", "age": 31, "email": { "work": "bob@example.com", "private": "bob@example.org" } } +{ "banana": "yellow", "tomato": "red", "broccoli": "green" } +{ "foo": "bar", "complex key": "qrx" } +{ "foo": { "bar": true } } +-- End -- + +-- Testcase -- +{% + // An empty object can be declared using a pair of curly brackets + empty_obj = { }; + + // It is also possible to use JSON notation to declare an object + json_obj = { + "name": "Bob", + "age": 31, + "email": { + "work": "bob@example.com", + "private": "bob@example.org" + } + }; + + // Declaring an object in JavaScript notation is supported as well + another_obj = { + banana: "yellow", + tomato: "red", + broccoli: "green" + }; + + // Mixing styles is allowed too + third_obj = { + foo: "bar", + "complex key": "qrx" + }; + + // Important caveat: when nesting objects, ensure that curly brackets + // are separated by space or newline to avoid interpretation as + // expression block tag! + nested_obj = { foo: { bar: true } }; // <-- mind the space in "} }" + + // Printing (or stringifying) objects will return their JSON representation + print(empty_obj, "\n"); + print(json_obj, "\n"); + print(another_obj, "\n"); + print(third_obj, "\n"); + print(nested_obj, "\n"); +%} +-- End -- + + +Additionally, utpl implements ES6-like spread operators to allow shallow copying +of object properties into other objects. + +-- Expect stdout -- +{ "foo": true, "bar": false } +{ "foo": true, "bar": false, "baz": 123, "qrx": 456 } +{ "foo": false, "bar": true, "baz": 123, "qrx": 456 } +{ "foo": true, "bar": false } +{ "foo": true, "bar": false, "level2": { "baz": 123, "qrx": 456 } } +{ "foo": true, "bar": false, "0": 7, "1": 8, "2": 9 } +-- End -- + +-- Testcase -- +{% + o1 = { foo: true, bar: false }; + o2 = { baz: 123, qrx: 456 }; + arr = [7, 8, 9]; + + print(join("\n", [ + // copying one object into another + { ...o1 }, + + // combining two objects + { ...o1, ...o2 }, + + // copying object and override properties + { ...o1, ...o2, foo: false, bar: true }, + + // default properties overwritten by spread operator + { foo: 123, bar: 456, ...o1 }, + + // nested spread operators + { ...o1, level2: { ...o2 } }, + + // merging array into objects + { ...o1, ...arr } + ]), "\n"); +%} +-- End -- + + +ES2015 short hand property notation is supported as well. + +-- Expect stdout -- +{ "a": 123, "b": true, "c": "test" } +-- End -- + +-- Testcase -- +{% + a = 123; + b = true; + c = "test"; + + o = { a, b, c }; + + print(o, "\n"); +%} +-- End -- + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ':' +In line 2, byte 14: + + ` o = { "foo" };` + Near here ------^ + + +-- End -- + +-- Testcase -- +{% + o = { "foo" }; +%} +-- End -- + + +ES2015 computed property names are supported. + +-- Expect stdout -- +{ "test": true, "hello": false, "ABC": 123 } +-- End -- + +-- Testcase -- +{% + s = "test"; + o = { + [s]: true, + ["he" + "llo"]: false, + [uc("abc")]: 123 + }; + + print(o, "\n"); +%} +-- End -- + +-- Expect stderr -- +Syntax error: Expecting expression +In line 2, byte 10: + + ` o1 = { []: true };` + Near here --^ + + +Syntax error: Unexpected token +Expecting ']' +In line 3, byte 14: + + ` o2 = { [true, false]: 123 };` + Near here ------^ + + +-- End -- + +-- Testcase -- +{% + o1 = { []: true }; + o2 = { [true, false]: 123 }; +%} +-- End -- diff --git a/tests/custom/00_syntax/14_array_literals b/tests/custom/00_syntax/14_array_literals new file mode 100644 index 0000000..941ee4a --- /dev/null +++ b/tests/custom/00_syntax/14_array_literals @@ -0,0 +1,82 @@ +The utpl script language supports declaring arrays using JSON notation. + +-- Expect stdout -- +[ ] +[ "first", "second", 123, [ "a", "nested", "array" ], { "a": "nested object" } ] +-- End -- + +-- Testcase -- +{% + // An empty array can be declared using a pair of square brackets + empty_array = [ ]; + + // JSON notation is used to declare an array with contents + json_array = [ + "first", + "second", + 123, + [ "a", "nested", "array" ], + { a: "nested object" } + ]; + + // Printing (or stringifying) arrays will return their JSON representation + print(empty_array, "\n"); + print(json_array, "\n"); +-- End -- + + +Additionally, utpl implements ES6-like spread operators to allow shallow copying +of array values into other arrays. + +-- Expect stdout -- +[ 1, 2, 3 ] +[ 1, 2, 3, 4, 5, 6 ] +[ 1, 2, 3, 4, 5, 6, false, true ] +[ 1, 2, 3, false, true, 4, 5, 6 ] +[ 1, 2, 3, [ 4, 5, 6 ] ] +-- End -- + +-- Testcase -- +{% + a1 = [ 1, 2, 3 ]; + a2 = [ 4, 5, 6 ]; + + print(join("\n", [ + // copying one array into another + [ ...a1 ], + + // combining two arrays + [ ...a1, ...a2 ], + + // copying array and append values + [ ...a1, ...a2, false, true ], + + // copy array and interleave values + [ ...a1, false, true, ...a2 ], + + // nested spread operators + [ ...a1, [ ...a2 ] ] + ]), "\n"); +%} +-- End -- + +Contrary to merging arrays into objects, objects cannot be merged into arrays. + +-- Expect stderr -- +Type error: ({ "foo": true, "bar": false }) is not iterable +In line 5, byte 21: + + ` print([ ...arr, ...obj ], "\n");` + Near here -------------^ + + +-- End -- + +-- Testcase -- +{% + arr = [ 1, 2, 3 ]; + obj = { foo: true, bar: false }; + + print([ ...arr, ...obj ], "\n"); +%} +-- End -- diff --git a/tests/custom/00_syntax/15_function_declarations b/tests/custom/00_syntax/15_function_declarations new file mode 100644 index 0000000..4257dd6 --- /dev/null +++ b/tests/custom/00_syntax/15_function_declarations @@ -0,0 +1,164 @@ +Function declarations follow the ECMAScript 5 syntax. Functions can be +declared anonymously, which is useful for "throw-away" functions such +as sort or filter callbacks or for building objects or arrays of function +values. + +If functions are declared with a name, the resulting function value is +automatically assigned under the given name to the current scope. + +When function values are stringifed, the resulting string will describe +the declaration of the function. + +Nesting function declarations is possible as well. + + +-- Expect stdout -- +function() { ... } +function test_fn(a, b) { ... } +function test2_fn(a, b) { ... } + +A function declaration using the alternative syntax: +The function was called with arguments 123 and 456. + +-- End -- + +-- Testcase -- +{% + // declare an anonymous function and + // assign resulting value + anon_fn = function() { + return "test"; + }; + + // declare a named function + function test_fn(a, b) { + return a + b; + } + + // nesting functions is legal + function test2_fn(a, b) { + function test3_fn(a, b) { + return a * b; + } + + return a + test3_fn(a, b); + } + + print(anon_fn, "\n"); + print(test_fn, "\n"); + print(test2_fn, "\n"); +%} + +A function declaration using the alternative syntax: +{% function test3_fn(a, b): %} +The function was called with arguments {{ a }} and {{ b }}. +{% endfunction %} +{{ test3_fn(123, 456) }} +-- End -- + + +Additionally, utpl implements ES6-like "rest" argument syntax to declare +variadic functions. + +-- Expect stdout -- +function non_variadic(a, b, c, d, e) { ... } +[ 1, 2, 3, 4, 5 ] +function variadic_1(a, b, ...args) { ... } +[ 1, 2, [ 3, 4, 5 ] ] +function variadic_2(...args) { ... } +[ [ 1, 2, 3, 4, 5 ] ] +-- End -- + +-- Testcase -- +{% + // ordinary, non-variadic function + function non_variadic(a, b, c, d, e) { + return [ a, b, c, d, e ]; + } + + // fixed amount of arguments with variable remainder + function variadic_1(a, b, ...args) { + return [ a, b, args ]; + } + + // only variable arguments + function variadic_2(...args) { + return [ args ]; + } + + print(join("\n", [ + non_variadic, + non_variadic(1, 2, 3, 4, 5), + variadic_1, + variadic_1(1, 2, 3, 4, 5), + variadic_2, + variadic_2(1, 2, 3, 4, 5) + ]), "\n"); +%} +-- End -- + + +Complementary to the "rest" argument syntax, the spread operator may be +used in function call arguments to pass arrays of values as argument list. + +-- Expect stdout -- +[ 1, 2, 3, 4, 5, 6 ] +[ 4, 5, 6, 1, 2, 3 ] +[ 1, 2, 3, 1, 2, 3 ] +[ 1, 2, 3 ] +-- End -- + +-- Testcase -- +{% + arr = [ 1, 2, 3 ]; + + function test(...args) { + return args; + } + + print(join("\n", [ + test(...arr, 4, 5, 6), + test(4, 5, 6, ...arr), + test(...arr, ...arr), + test(...arr) + ]), "\n"); +%} +-- End -- + + +Rest arguments may be only used once in a declaration and they must always +be the last item in the argument list. + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ')' +In line 2, byte 26: + + ` function illegal(...args, ...args2) {}` + Near here ------------------^ + + +-- End -- + +-- Testcase -- +{% + function illegal(...args, ...args2) {} +%} +-- End -- + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ')' +In line 2, byte 26: + + ` function illegal(...args, a, b) {}` + Near here ------------------^ + + +-- End -- + +-- Testcase -- +{% + function illegal(...args, a, b) {} +%} +-- End -- diff --git a/tests/custom/00_syntax/16_for_loop b/tests/custom/00_syntax/16_for_loop new file mode 100644 index 0000000..67edc21 --- /dev/null +++ b/tests/custom/00_syntax/16_for_loop @@ -0,0 +1,299 @@ +Two for-loop variants are supported: a C-style counting for loop +consisting of an initialization expression, a test condition +and a step expression and a for-in-loop variant which allows +enumerating properties of objects or items of arrays. + +Additionally, utpl supports an alternative syntax suitable for +template block tags. + + +-- Expect stdout -- +A simple counting for-loop: +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 + +If the loop body consists of only one statement, the curly braces +may be omitted: +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 + +Any of the init-, test- and increment expressions may be omitted. + +Loop without initialization statement: +Iteration null +Iteration 1 +Iteration 2 + +Loop without test statement: +Iteration 0 +Iteration 1 +Iteration 2 + +Loop without init-, test- or increment statement: +Iteration 1 +Iteration 2 +Iteration 3 + +For-in loop enumerating object properties: +Key: foo +Key: bar + +For-in loop enumerating array items: +Item: one +Item: two +Item: three + +A counting for-loop using the alternative syntax: +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 + +A for-in loop using the alternative syntax: +Item 123 +Item 456 +Item 789 + +For-in and counting for loops may declare variables: +Iteration 0 +Iteration 1 +Iteration 2 + +Item 123 +Item 456 +Item 789 +-- End -- + +-- Testcase -- +A simple counting for-loop: +{% + for (i = 0; i < 10; i++) { + print("Iteration "); + print(i); + print("\n"); + } +%} + +If the loop body consists of only one statement, the curly braces +may be omitted: +{% + for (i = 0; i < 10; i++) + print("Iteration ", i, "\n"); +%} + +Any of the init-, test- and increment expressions may be omitted. + +Loop without initialization statement: +{% + for (; j < 3; j++) + print("Iteration " + j + "\n"); +%} + +Loop without test statement: +{% + for (j = 0;; j++) { + if (j == 3) + break; + + print("Iteration ", j, "\n"); + } +%} + +Loop without init-, test- or increment statement: +{% + for (;;) { + if (k++ == 3) + break; + + print("Iteration ", k, "\n"); + } +%} + +For-in loop enumerating object properties: +{% + obj = { foo: true, bar: false }; + for (key in obj) + print("Key: ", key, "\n"); +%} + +For-in loop enumerating array items: +{% + arr = [ "one", "two", "three" ]; + for (item in arr) + print("Item: ", item, "\n"); +%} + +A counting for-loop using the alternative syntax: +{% for (x = 0; x < 10; x++): -%} +Iteration {{ x }} +{% endfor %} + +A for-in loop using the alternative syntax: +{% for (n in [123, 456, 789]): -%} +Item {{ n }} +{% endfor %} + +For-in and counting for loops may declare variables: +{% for (let i = 0; i < 3; i++): %} +Iteration {{ i }} +{% endfor %} + +{% for (let n in [123, 456, 789]): %} +Item {{ n }} +{% endfor %} +-- End -- + + +By specifying two loop variables in for-in loop expressions, keys +and values can be iterated simultaneously. + +-- Expect stdout -- +true +false +123 +456 +[ 0, true ] +[ 1, false ] +[ 2, 123 ] +[ 3, 456 ] +foo +bar +baz +qrx +[ "foo", true ] +[ "bar", false ] +[ "baz", 123 ] +[ "qrx", 456 ] +-- End -- + +-- Testcase -- +{% + let arr = [ true, false, 123, 456 ]; + let obj = { foo: true, bar: false, baz: 123, qrx: 456 }; + + // iterating arrays with one loop variable yields the array values + for (let x in arr) + print(x, "\n"); + + // iterating arrays with two loop variables yields the array indexes + // and their corresponding values + for (let x, y in arr) + print([x, y], "\n"); + + // iterating objects with one loop variable yields the object keys + for (let x in obj) + print(x, "\n"); + + // iterating objects with two loop variables yields the object keys + // and their corresponding values + for (let x, y in obj) + print([x, y], "\n"); +%} +-- End -- + + +Ensure that for-in loop expressions with more than two variables are +rejected. + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ';' +In line 2, byte 24: + + ` for (let x, y, z in {})` + Near here ----------------^ + + +-- End -- + +-- Testcase -- +{% + for (let x, y, z in {}) + ; +%} +-- End -- + + +Ensure that assignments in for-in loop expressions are rejected. + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ';' +In line 2, byte 25: + + ` for (let x = 1, y in {})` + Near here -----------------^ + + +-- End -- + +-- Testcase -- +{% + for (let x = 1, y in {}) + ; +%} +-- End -- + + +Ensure that too short for-in loop expressions are rejected (1/2). + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ';' +In line 2, byte 12: + + ` for (let x)` + Near here ----^ + + +-- End -- + +-- Testcase -- +{% + for (let x) + ; +%} +-- End -- + + +Ensure that too short for-in loop expressions are rejected (2/2). + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ';' +In line 2, byte 15: + + ` for (let x, y)` + Near here -------^ + + +-- End -- + +-- Testcase -- +{% + for (let x, y) + ; +%} +-- End -- diff --git a/tests/custom/00_syntax/17_while_loop b/tests/custom/00_syntax/17_while_loop new file mode 100644 index 0000000..1e68d6b --- /dev/null +++ b/tests/custom/00_syntax/17_while_loop @@ -0,0 +1,71 @@ +Utpl implements C-style while loops which run as long as the condition +is fulfilled. + +Like with for-loops, an alternative syntax form suitable for template +blocks is supported. + + +-- Expect stdout -- +A simple counting while-loop: +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 + +If the loop body consists of only one statement, the curly braces +may be omitted: +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 + +A counting while-loop using the alternative syntax: +Iteration 0 +Iteration 1 +Iteration 2 +Iteration 3 +Iteration 4 +Iteration 5 +Iteration 6 +Iteration 7 +Iteration 8 +Iteration 9 +-- End -- + +-- Testcase -- +A simple counting while-loop: +{% + i = 0; + while (i < 10) { + print("Iteration "); + print(i); + print("\n"); + i++; + } +%} + +If the loop body consists of only one statement, the curly braces +may be omitted: +{% + i = 0; + while (i < 10) + print("Iteration ", i++, "\n"); +%} + +A counting while-loop using the alternative syntax: +{% while (x < 10): -%} +Iteration {{ "" + x++ }} +{% endwhile %} +-- End -- diff --git a/tests/custom/00_syntax/18_if_condition b/tests/custom/00_syntax/18_if_condition new file mode 100644 index 0000000..9e02767 --- /dev/null +++ b/tests/custom/00_syntax/18_if_condition @@ -0,0 +1,121 @@ +Utpl implements C-style if/else conditions and ?: ternary statements. + +Like with for- and while-loops, an alternative syntax form suitable +for template blocks is supported. + + +-- Expect stdout -- +This should print "one": +one + +This should print "two": +two + +Multiple conditions can be used by chaining if/else statements: +three + +If the conditional block consists of only one statement, the curly +braces may be omitted: +two + +An if-condition using the alternative syntax: +Variable x has another value. + + +An if-condition using the special "elif" keyword in alternative syntax mode: +Variable x was set to five. + + +Ternary expressions function similar to if/else statements but +only allow for a single expression in the true and false branches: +Variable x is one +-- End -- + +-- Testcase -- +This should print "one": +{% + x = 0; + + if (x == 0) { + print("one"); + } + else { + print("two"); + } +%} + + +This should print "two": +{% + x = 1; + + if (x == 0) { + print("one"); + } + else { + print("two"); + } +%} + + +Multiple conditions can be used by chaining if/else statements: +{% + x = 2; + + if (x == 0) { + print("one"); + } + else if (x == 1) { + print("two"); + } + else if (x == 2) { + print("three"); + } + else { + print("four"); + } +%} + + +If the conditional block consists of only one statement, the curly +braces may be omitted: +{% + x = 5; + + if (x == 0) + print("one"); + else + print("two"); +%} + + +An if-condition using the alternative syntax: +{% if (x == 1): -%} +Variable x was set to one. +{% else -%} +Variable x has another value. +{% endif %} + + +An if-condition using the special "elif" keyword in alternative syntax mode: +{% if (x == 0): -%} +Variable x was set to zero. +{% elif (x == 1): -%} +Variable x was set to one. +{% elif (x == 5): -%} +Variable x was set to five. +{% else -%} +Variable x has another value. +{% endif %} + + +Ternary expressions function similar to if/else statements but +only allow for a single expression in the true and false branches: +{% + x = 1; + s = (x == 1) ? "Variable x is one" : "Variable x has another value"; + + print(s); +%} + +-- End -- diff --git a/tests/custom/00_syntax/19_arrow_functions b/tests/custom/00_syntax/19_arrow_functions new file mode 100644 index 0000000..102c527 --- /dev/null +++ b/tests/custom/00_syntax/19_arrow_functions @@ -0,0 +1,124 @@ +Besides the ordinary ES5-like function declarations, utpl supports ES6 inspired +arrow function syntax as well. Such arrow functions are useful for callbacks to functions such as replace(), map() or filter(). + +-- Expect stdout -- +() => { ... } +test +(a, b) => { ... } +3 +(...args) => { ... } +15 +(a) => { ... } +10 +(a) => { ... } +36 +-- End -- + +-- Testcase -- +{% + + // assign arrow function to variable + test1_fn = () => { + return "test"; + }; + + // assign arrow function with parameters + test2_fn = (a, b) => { + return a + b; + }; + + // nesting functions is legal + test3_fn = (...args) => { + nested_fn = (a, b) => { + return a * b; + }; + + return args[0] + nested_fn(args[0], args[1]); + }; + + // parentheses may be omitted if arrow function takes only one argument + test4_fn = a => { + a * 2; + }; + + // curly braces may be omitted if function body is a single expression + test5_fn = a => a * a; + + print(join("\n", [ + test1_fn, + test1_fn(), + test2_fn, + test2_fn(1, 2), + test3_fn, + test3_fn(3, 4), + test4_fn, + test4_fn(5), + test5_fn, + test5_fn(6) + ]), "\n"); +%} +-- End -- + + +While the main advantage of arrow functions is the compact syntax, another +important difference to normal functions is the "this" context behaviour - +arrow functions do not have an own "this" context and simply inherit it from +the outer calling scope. + +-- Expect stdout -- +this is set to obj: true +arrow function uses outher this: true +normal function has own this: true +arrow function as method has no this: true +-- End -- + +-- Testcase -- +{% + obj = { + method: function() { + let that = this; + let arr = () => { + print("arrow function uses outher this: ", that == this, "\n"); + }; + let fn = function() { + print("normal function has own this: ", that != this, "\n"); + }; + + print("this is set to obj: ", this == obj, "\n"); + + arr(); + fn(); + }, + + arrowfn: () => { + print("arrow function as method has no this: ", this == null, "\n"); + } + }; + + obj.method(); + obj.arrowfn(); +%} +-- End -- + + +Due to the difficulty of recognizing arrow function expressions with an LR(1) +grammar the parser has to use a generic expression rule on the lhs argument list +and verify that it does not contain non-label nodes while building the ast. The +subsequent testcase asserts that case. + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ';' +In line 2, byte 10: + + ` (a + 1) => { print("test\n") }` + Near here --^ + + +-- End -- + +-- Testcase -- +{% + (a + 1) => { print("test\n") } +%} +-- End -- diff --git a/tests/custom/00_syntax/20_list_expressions b/tests/custom/00_syntax/20_list_expressions new file mode 100644 index 0000000..d5ba459 --- /dev/null +++ b/tests/custom/00_syntax/20_list_expressions @@ -0,0 +1,45 @@ +Similar to ES5, utpl's language grammar allows comma separated list expressions +in various contexts. Unless such lists happen to be part of a function call +or array construction expression, only the last result of such an expression +list should be used while still evaluating all sub-expressions, triggering +side effects such as function calls or variable assignments. + +-- Expect stdout -- +4 +[ 1, 3 ] +{ "a": true, "b": 1 } +function call +[ "test", "assigment" ] +true +true +true +[ 2, 3 ] +-- End -- + +-- Testcase -- +{% + // only the last value is considered + print(1 + (2, 3), "\n"); + + // in array constructors, parenthesized lists are reduced to the last value + print([ (0, 1), (2, 3) ], "\n"); + + // in object constructors, parenthesized lists are reduced to the last value + print({ a: (false, true), b: (0, 1) }, "\n"); + + // all list expressions are evaluated and may have side effects, even if + // results are discareded + x = (print("function call\n"), y = "assigment", "test"); + print([x, y], "\n"); + + // property access operates on the last value of a parenthesized list expression + print(({foo: false}, {foo: true}).foo, "\n"); + print(({foo: false}, {foo: true})["foo"], "\n"); + + // computed property access uses the last list expression value + print(({foo: true})["bar", "baz", "foo"], "\n"); + + // same list semantics apply to function call parameters + ((...args) => print(args, "\n"))((1, 2), 3); +%} +-- End -- diff --git a/tests/custom/00_syntax/21_regex_literals b/tests/custom/00_syntax/21_regex_literals new file mode 100644 index 0000000..3af53bb --- /dev/null +++ b/tests/custom/00_syntax/21_regex_literals @@ -0,0 +1,89 @@ +Regex literals are enclosed in forward slashes and may contain zero +or more trailing flag characters. Interpretation of escape sequences +within regular expression literals is subject of the underlying +regular expression engine. + +-- Expect stdout -- +[ "/Hello world/", "/test/gis", "/test/g", "/test1 \\\/ test2/", "/\\x31\n\\.\u0007\b\\c\\u2600\\\\/" ] +-- End -- + +-- Testcase -- +{% + print([ + /Hello world/, // A very simple expression + /test/gsi, // Regular expression flags + /test/gg, // Repeated flags + /test1 \/ test2/, // Escaped forward slash + /\x31\n\.\a\b\c\u2600\\/ // Ensure that escape sequences are passed as-is + ], "\n"); +%} +-- End -- + + +Testing regular expression type. + +-- Expect stdout -- +object +-- End -- + +-- Testcase -- +{{ type(/foo/) }} +-- End -- + + +Testing invalid flag characters. + +-- Expect stderr -- +Syntax error: Unexpected token +Expecting ';' +In line 2, byte 8: + + ` /test/x` + ^-- Near here + + +-- End -- + +-- Testcase -- +{% + /test/x +%} +-- End -- + + +Testing unclosed regular expression. + +-- Expect stderr -- +Syntax error: Unterminated string +In line 2, byte 2: + + ` /foo \/` + ^-- Near here + + +-- End -- + +-- Testcase -- +{% + /foo \/ +%} +-- End -- + + +Testing regex compilation errors. + +-- Expect stderr -- +Syntax error: Unmatched \{ +In line 2, byte 3: + + ` /foo {/` + ^-- Near here + + +-- End -- + +-- Testcase -- +{% + /foo {/ +%} +-- End -- diff --git a/tests/custom/01_arithmetic/00_value_conversion b/tests/custom/01_arithmetic/00_value_conversion new file mode 100644 index 0000000..c44ad00 --- /dev/null +++ b/tests/custom/01_arithmetic/00_value_conversion @@ -0,0 +1,125 @@ +Performing unary plus or minus, or performing arithmetic operations may +implicitly convert non-numeric values to numeric ones. + +If an addition is performed and either operand is a string, the other +operand is converted into a string as well and the addition result is a +concatenated string consisting of the two operand values. + +-- Expect stdout -- +HelloWorld +12 +34 +true +false +null +{ "some": "object" } +[ "some", "array" ] +function() { ... } +1.2 +Infinity +124 +123 +123 +NaN +NaN +NaN +124.2 +Infinity +1 +0 +0 +NaN +NaN +NaN +1.2 +Infinity +123 +12.3 +NaN +-1 +0 +0 +NaN +NaN +NaN +-1.2 +-Infinity +-123 +-12.3 +NaN +4.2 +9.6 +-- End -- + +-- Testcase -- +{% + print(join("\n", [ + + // Adding two strings concatenates them: + "Hello" + "World", + + // Adding a number to a string results in a string: + "1" + 2, + + // Adding a string to a number results in a string: + 3 + "4", + + // Adding any non-string value to a string or vice versa will + // force stringification of the non-string value + "" + true, + "" + false, + "" + null, + "" + { some: "object" }, + "" + [ "some", "array" ], + "" + function() {}, + "" + 1.2, + "" + (1 / 0), + + // Adding a numeric value to a non-string, non-numeric value + // or vice versa will convert the non-numeric argument to a + // number + 123 + true, + 123 + false, + 123 + null, + 123 + { some: "object" }, + 123 + [ "some", "array" ], + 123 + function() {}, + 123 + 1.2, + 123 + (1 / 0), + + // The unary "+" operator follows the same logic as adding a + // non-numeric, non-string value to a numeric one. Additionally + // the unary plus forces conversion of string values into numbers + +true, + +false, + +null, + +{ some: "object" }, + +[ "some", "array" ], + +function() {}, + +1.2, + +(1 / 0), + +"123", + +"12.3", + +"this is not a number", + + // The unary "-" operator functions like the unary "+" one and + // it additionally returns the negation of the numeric value + -true, + -false, + -null, + -{ some: "object" }, + -[ "some", "array" ], + -function() {}, + -1.2, + -(1 / 0), + -"123", + -"12.3", + -"this is not a number", + + // Adding a double to an integer or vice versa will force the + // result to a double as well + 1.2 + 3, + 4 + 5.6, + + "" ])); +-- End -- diff --git a/tests/custom/01_arithmetic/01_division b/tests/custom/01_arithmetic/01_division new file mode 100644 index 0000000..d4a2adb --- /dev/null +++ b/tests/custom/01_arithmetic/01_division @@ -0,0 +1,53 @@ +While arithmetic divisions generally follow the value conversion rules +outlined in the "00_value_conversion" test case, a number of additional +constraints apply. + +-- Expect stdout -- +Division by zero yields Infinity: +1 / 0 = Infinity + +Division by Infinity yields zero: +1 / Infinity = 0 + +Dividing Infinity yields Infinity: +Infinity / 1 = Infinity + +Dividing Infinity by Infinity yields NaN: +Infinity / Infinity = NaN + +If either operand is NaN, the result is NaN: +1 / NaN = NaN +NaN / 1 = NaN + +If both operands are integers, integer division is performed: +10 / 3 = 3 + +If either operand is a double, double division is performed: +10.0 / 3 = 3.33333 +10 / 3.0 = 3.33333 +-- End -- + +-- Testcase -- +Division by zero yields Infinity: +1 / 0 = {{ 1 / 0 }} + +Division by Infinity yields zero: +1 / Infinity = {{ 1 / Infinity }} + +Dividing Infinity yields Infinity: +Infinity / 1 = {{ Infinity / 1 }} + +Dividing Infinity by Infinity yields NaN: +Infinity / Infinity = {{ Infinity / Infinity }} + +If either operand is NaN, the result is NaN: +1 / NaN = {{ 1 / NaN }} +NaN / 1 = {{ NaN / 1 }} + +If both operands are integers, integer division is performed: +10 / 3 = {{ 10 / 3 }} + +If either operand is a double, double division is performed: +10.0 / 3 = {{ 10.0 / 3 }} +10 / 3.0 = {{ 10 / 3.0 }} +-- End -- diff --git a/tests/custom/01_arithmetic/02_modulo b/tests/custom/01_arithmetic/02_modulo new file mode 100644 index 0000000..244d624 --- /dev/null +++ b/tests/custom/01_arithmetic/02_modulo @@ -0,0 +1,32 @@ +The utpl language supports modulo divisions, however they're only defined +for integer values. + +-- Expect stdout -- +If both operands are integers or convertible to integers, +the modulo division yields the remainder: +10 % 4 = 2 +"10" % 4 = 2 +10 % "4" = 2 +"10" % "4" = 2 + +If either operand is a double value, the modulo operation +yields NaN: +10.0 % 4 = NaN +10 % 4.0 = NaN +"10.0" % 4 = NaN +-- End -- + +-- Testcase -- +If both operands are integers or convertible to integers, +the modulo division yields the remainder: +10 % 4 = {{ 10 % 4 }} +"10" % 4 = {{ "10" % 4 }} +10 % "4" = {{ 10 % 4 }} +"10" % "4" = {{ "10" % "4" }} + +If either operand is a double value, the modulo operation +yields NaN: +10.0 % 4 = {{ 10.0 % 4 }} +10 % 4.0 = {{ 10 % 4.0 }} +"10.0" % 4 = {{ "10.0" % 4 }} +-- End -- diff --git a/tests/custom/01_arithmetic/03_bitwise b/tests/custom/01_arithmetic/03_bitwise new file mode 100644 index 0000000..faf4ffd --- /dev/null +++ b/tests/custom/01_arithmetic/03_bitwise @@ -0,0 +1,54 @@ +Utpl implements C-style bitwise operations. One detail is that these operations +coerce their operands to signed 64bit integer values internally. + +-- Expect stdout -- +Left shift: +10 << 2 = 40 +3.3 << 4.1 = 48 + +Right shift: +10 >> 2 = 2 +3.3 >> 4.1 = 0 + +Bitwise and: +1234 & 200 = 192 +120.3 & 54.3 = 48 + +Bitwise xor: +1234 ^ 200 = 1050 +120.3 ^ 54.3 = 78 + +Bitwise or: +1234 | 200 = 1242 +120.3 | 54.3 = 126 + +Complement: +~0 = -1 +~10.4 = -11 +-- End -- + +-- Testcase -- +Left shift: +10 << 2 = {{ 10 << 2 }} +3.3 << 4.1 = {{ 3.3 << 4.1 }} + +Right shift: +10 >> 2 = {{ 10 >> 2 }} +3.3 >> 4.1 = {{ 3.3 >> 4.1 }} + +Bitwise and: +1234 & 200 = {{ 1234 & 200 }} +120.3 & 54.3 = {{ 120.3 & 54.3 }} + +Bitwise xor: +1234 ^ 200 = {{ 1234 ^ 200 }} +120.3 ^ 54.3 = {{ 120.3 ^ 54.3 }} + +Bitwise or: +1234 | 200 = {{ 1234 | 200 }} +120.3 | 54.3 = {{ 120.3 | 54.3 }} + +Complement: +~0 = {{ ~0 }} +~10.4 = {{ ~10.4 }} +-- End -- diff --git a/tests/custom/01_arithmetic/04_inc_dec b/tests/custom/01_arithmetic/04_inc_dec new file mode 100644 index 0000000..ae50ceb --- /dev/null +++ b/tests/custom/01_arithmetic/04_inc_dec @@ -0,0 +1,49 @@ +Utpl implements C-style pre- and postfix increment and decrement operators. + +Pre-increment or -decrement operations first mutate the value and then return +the resulting value while post-increment or -decrement operations first return +the initial value and then mutate the operand. + +Since the decrement and increment operators mutate their operand, they +may only be applied to variables, not constant literal expressions. + +If an undefined variable is incremented or decremented, its initial value +is assumed to be "0". + +If a non-numeric value is incremented or decremented, it is converted to a +number first. If the value is not convertible, the result of the increment +or decrement operation is NaN. + +-- Expect stdout -- +Incrementing a not existing variable assumes "0" as initial value: + + - Postfix increment result: 0, value after: 1 + - Prefix increment result: 1, value after: 1 + - Postfix decrement result: 0, value after: -1 + - Prefix decrement result: -1, value after: -1 + +Incrementing a non-numeric value will convert it to a number: + +124 +3.5 +2 +NaN +-- End -- + +-- Testcase -- +Incrementing a not existing variable assumes "0" as initial value: + + - Postfix increment result: {{ "" + a++ }}, value after: {{ a }} + - Prefix increment result: {{ "" + ++b }}, value after: {{ b }} + - Postfix decrement result: {{ "" + c-- }}, value after: {{ c }} + - Prefix decrement result: {{ "" + --d }}, value after: {{ d }} + +Incrementing a non-numeric value will convert it to a number: + +{% + n = "123"; n++; print(n, "\n"); + n = "4.5"; n--; print(n, "\n"); + n = true; n++; print(n, "\n"); + n = { some: "object" }; n--; print(n, "\n"); +%} +-- End -- diff --git a/tests/custom/02_runtime/00_scoping b/tests/custom/02_runtime/00_scoping new file mode 100644 index 0000000..5fadf43 --- /dev/null +++ b/tests/custom/02_runtime/00_scoping @@ -0,0 +1,161 @@ +Utpl implements function scoping, make sure that let variables are +invisible outside of the function scope. + +-- Expect stdout -- +a_global=true +a_local=true + +b_global=true +b_local=false + +c_global=true +c_local=false + + +When seting a nonlocal variable, it is set in the nearest parent +scope containing the variable or in the root scope if the variable +was not found. + +x=2 +y= +z=1 + + +Variables implicitly declared by for-in or counting for loops follow the same +scoping rules. + +inner2 f_a=3 +inner2 f_b= +inner2 f_c=3 +inner2 f_d= +inner2 f_e=3 + +inner f_a=3 +inner f_b= +inner f_c=3 +inner f_d= +inner f_e=3 + +outer f_a=3 +outer f_b= +outer f_c= +outer f_d= +outer f_e=3 +-- End -- + +-- Testcase -- +{% + a_global = true; + let a_local = true; + + function test() { + b_global = true; + let b_local = true; + + function test2() { + c_global = true; + let c_local = true; + } + + test2(); + } + + test(); +-%} + +a_global={{ !!a_global }} +a_local={{ !!a_local }} + +b_global={{ !!b_global }} +b_local={{ !!b_local }} + +c_global={{ !!c_global }} +c_local={{ !!c_local }} + + +When seting a nonlocal variable, it is set in the nearest parent +scope containing the variable or in the root scope if the variable +was not found. + +{% + x = 1; + + function scope1() { + x = 2; + let y; + + function scope2() { + // this does not set "y" in the root scope but overwrites the + // variable declared in the "scope1" function scope. + y = 2; + + // this sets "z" in the root scope because it was not declared + // anywhere yet + z = 1; + } + + scope2(); + } + + scope1(); +-%} + +x={{ x }} +y={{ y }} +z={{ z }} + + +Variables implicitly declared by for-in or counting for loops follow the same +scoping rules. + +{% + function scope3() { + // f_a is not declared local and be set in the root scope + for (f_a = 1; f_a < 3; f_a++) + ; + + for (let f_b = 1; f_b < 3; f_b++) + ; + + let f_c; + + function scope4() { + // f_c is not declared local but declared in the parent scope, it + // will be set there + for (f_c in [1, 2, 3]) + ; + + for (let f_d in [1, 2, 3]) + ; + + // f_e is not declared, it will be set in the root scope + for (f_e in [1, 2, 3]) + ; + + print("inner2 f_a=", f_a, "\n"); + print("inner2 f_b=", f_b, "\n"); + print("inner2 f_c=", f_c, "\n"); + print("inner2 f_d=", f_d, "\n"); + print("inner2 f_e=", f_e, "\n"); + print("\n"); + } + + scope4(); + + print("inner f_a=", f_a, "\n"); + print("inner f_b=", f_b, "\n"); + print("inner f_c=", f_c, "\n"); + print("inner f_d=", f_d, "\n"); + print("inner f_e=", f_e, "\n"); + print("\n"); + } + + scope3(); + + print("outer f_a=", f_a, "\n"); + print("outer f_b=", f_b, "\n"); + print("outer f_c=", f_c, "\n"); + print("outer f_d=", f_d, "\n"); + print("outer f_e=", f_e, "\n"); +%} +-- End -- diff --git a/tests/custom/02_runtime/01_break_continue b/tests/custom/02_runtime/01_break_continue new file mode 100644 index 0000000..a27d072 --- /dev/null +++ b/tests/custom/02_runtime/01_break_continue @@ -0,0 +1,50 @@ +The "break" and "continue" statements allow to abort a running loop or to +prematurely advance to the next cycle. + +-- Expect stdout -- +Testing break: + - Iteration 0 + - Iteration 1 + - Iteration 2 + - Iteration 3 + - Iteration 4 + - Iteration 5 + - Iteration 6 + - Iteration 7 + - Iteration 8 + - Iteration 9 + - Iteration 10 + +Testing continue: + - Iteration 0 + - Iteration 2 + - Iteration 4 + - Iteration 6 + - Iteration 8 +-- End -- + +-- Testcase -- +Testing break: +{% + let i = 0; + + while (true) { + print(" - Iteration ", i, "\n"); + + if (i == 10) + break; + + i++; + } +%} + +Testing continue: +{% + for (i = 0; i < 10; i++) { + if (i % 2) + continue; + + print(" - Iteration ", i, "\n"); + } +%} +-- End -- diff --git a/tests/custom/02_runtime/02_this b/tests/custom/02_runtime/02_this new file mode 100644 index 0000000..d8e85d2 --- /dev/null +++ b/tests/custom/02_runtime/02_this @@ -0,0 +1,50 @@ +The "this" object accesses the current function context. + +-- Expect stdout -- +true +true +-- End -- + +-- Testcase -- +{% + // Functions not invoked on objects have no this context + function test() { + return (this === null); + } + + // When invoked, "this" will point to the object containing the function + let o; + o = { + test: function() { + return (this === o); + } + }; + + print(test(), "\n"); + print(o.test(), "\n"); +%} +-- End -- + +Test that the context is properly restored if function call arguments are +dot or bracket expressions as well. + +-- Expect stdout -- +true +true +-- End -- + +-- Testcase -- +{% + let o; + o = { + test: function() { + return (this === o); + } + }; + + let dummy = { foo: true, bar: false }; + + print(o.test(dummy.foo, dummy.bar), "\n"); + print(o.test(dummy.foo, o.test(dummy.foo, dummy.bar)), "\n"); +%} +-- End -- diff --git a/tests/custom/02_runtime/03_try_catch b/tests/custom/02_runtime/03_try_catch new file mode 100644 index 0000000..751ca1d --- /dev/null +++ b/tests/custom/02_runtime/03_try_catch @@ -0,0 +1,138 @@ +Wrapping an exeptional operation in try {} catch {} allows handling the +resulting exception and to continue the execution flow. + +-- Expect stdout -- +Catched first exception. +Catched second exception: exception 2. +After exceptions. +-- End -- + +-- Testcase -- +{% + // A try-catch block that discards the exception information. + try { + die("exception 1"); + } + catch { + print("Catched first exception.\n"); + } + + // A try-catch block that captures the resulting exception in + // the given variable. + try { + die("exception 2"); + } + catch (e) { + print("Catched second exception: ", e, ".\n"); + } + + print("After exceptions.\n"); +%} +-- End -- + + +Ensure that exceptions are propagated through C function calls. + +-- Expect stderr -- +exception +In [anonymous function](), line 3, byte 18: + called from function replace ([C]) + called from anonymous function ([stdin]:4:3) + + ` die("exception");` + Near here -------------^ + + +-- End -- + +-- Testcase -- +{% + replace("test", "t", function(m) { + die("exception"); + }); +%} +-- End -- + + +Ensure that exception can be catched through C function calls. + +-- Expect stdout -- +Caught exception: exception +-- End -- + +-- Testcase -- +{% + try { + replace("test", "t", function(m) { + die("exception"); + }); + } + catch (e) { + print("Caught exception: ", e, "\n"); + } +%} +-- End -- + + +Ensure that exceptions are propagated through user function calls. + +-- Expect stderr -- +exception +In a(), line 3, byte 18: + called from function b ([stdin]:7:5) + called from function c ([stdin]:11:5) + called from anonymous function ([stdin]:14:4) + + ` die("exception");` + Near here -------------^ + + +-- End -- + +-- Testcase -- +{% + function a() { + die("exception"); + } + + function b() { + a(); + } + + function c() { + b(); + } + + c(); +%} +-- End -- + + +Ensure that exceptions can be caught in parent functions. + +-- Expect stdout -- +Caught exception: exception +-- End -- + +-- Testcase -- +{% + function a() { + die("exception"); + } + + function b() { + a(); + } + + function c() { + try { + b(); + } + catch (e) { + print("Caught exception: ", e, "\n"); + } + } + + c(); +%} +-- End -- diff --git a/tests/custom/02_runtime/04_switch_case b/tests/custom/02_runtime/04_switch_case new file mode 100644 index 0000000..4c1fc57 --- /dev/null +++ b/tests/custom/02_runtime/04_switch_case @@ -0,0 +1,325 @@ +Testing utpl switch statements. + + +1. Ensure that execution starts at the first matching case. + +-- Expect stdout -- +1a +-- End -- + +-- Testcase -- +{% + switch (1) { + case 1: + print("1a\n"); + break; + + case 1: + print("1b\n"); + break; + + case 2: + print("2\n"); + break; + } +%} +-- End -- + + +2. Ensure that default case is only used if no case matches, + even if declared first. + +-- Expect stdout -- +1 +default +-- End -- + +-- Testcase -- +{% + for (n in [1, 3]) { + switch (n) { + default: + print("default\n"); + break; + + case 1: + print("1\n"); + break; + + case 2: + print("2\n"); + break; + } + } +%} +-- End -- + + +3. Ensure that cases without break fall through into + subsequent cases. + +-- Expect stdout -- +1 +2 +default +1 +2 +-- End -- + +-- Testcase -- +{% + for (n in [1, 3]) { + switch (n) { + default: + print("default\n"); + + case 1: + print("1\n"); + + case 2: + print("2\n"); + } + } +%} +-- End -- + + +4. Ensure that a single default case matches. + +-- Expect stdout -- +default +default +-- End -- + +-- Testcase -- +{% + for (n in [1, 3]) { + switch (n) { + default: + print("default\n"); + } + } +%} +-- End -- + + +5. Ensure that duplicate default cases emit a syntax + error during parsing. + +-- Expect stderr -- +Syntax error: more than one switch default case +In line 6, byte 3: + + ` default:` + ^-- Near here + + +Syntax error: Expecting expression +In line 8, byte 2: + + ` }` + ^-- Near here + + +-- End -- + +-- Testcase -- +{% + switch (1) { + default: + print("default1\n"); + + default: + print("default2\n"); + } +%} +-- End -- + + +6. Ensure that case values use strict comparison. + +-- Expect stdout -- +b +b +-- End -- + +-- Testcase -- +{% + switch (1.0) { + case 1: + print("a\n"); + break; + + case 1.0: + print("b\n"); + break; + } + + switch ("123") { + case 123: + print("a\n"); + break; + + case "123": + print("b\n"); + break; + } +%} +-- End -- + + +7. Ensure that case values may be complex expressions. + +-- Expect stdout -- +2, 3, 1 +-- End -- + +-- Testcase -- +{% + switch (1) { + case a = 2, b = 3, c = 1: + print(join(", ", [ a, b, c ]), "\n"); + break; + } +%} +-- End -- + + +8. Ensure that empty switch statements are accepted by the + parser and that the test expression is evaluated. + +-- Expect stdout -- +true +-- End -- + +-- Testcase -- +{% + x = false; + + switch (x = true) { + + } + + print(x, "\n"); +%} +-- End -- + + +9. Ensure that `return` breaks out of switch statements. + +-- Expect stdout -- +one +two +-- End -- + +-- Testcase -- +{% + function test(n) { + switch (n) { + case 1: + return "one"; + + case 2: + return "two"; + + default: + return "three"; + } + } + + print(test(1), "\n"); + print(test(2), "\n"); +%} +-- End -- + + +10. Ensure that `continue` breaks out of switch statements. + +-- Expect stdout -- +one +two +-- End -- + +-- Testcase -- +{% + for (n in [1,2]) { + switch (n) { + case 1: + print("one\n"); + continue; + + case 2: + print("two\n"); + continue; + + default: + print("three\n"); + } + } +%} +-- End -- + + +11. Ensure that exceptions break out of switch statements. + +-- Expect stdout -- +one +-- End -- + +-- Expect stderr -- +Died +In test(), line 6, byte 8: + called from anonymous function ([stdin]:17:14) + + ` die();` + Near here ------^ + + +-- End -- + +-- Testcase -- +{% + function test(n) { + switch (n) { + case 1: + print("one\n"); + die(); + + case 2: + print("two\n"); + die(); + + default: + print("three\n"); + } + } + + print(test(1), "\n"); +%} +-- End -- + + +12. Ensure that consecutive cases values are properly handled. + +-- Expect stdout -- +three and four +-- End -- + +-- Testcase -- +{% + switch (3) { + case 1: + case 2: + print("one and two\n"); + break; + + case 3: + case 4: + print("three and four\n"); + break; + + default: + print("five\n"); + } +%} +-- End -- diff --git a/tests/custom/02_runtime/05_closure_scope b/tests/custom/02_runtime/05_closure_scope new file mode 100644 index 0000000..c59a433 --- /dev/null +++ b/tests/custom/02_runtime/05_closure_scope @@ -0,0 +1,35 @@ +Testing closure scopes. + + +1. Ensure that the declaring scope is retained in functions. + +-- Expect stdout -- +Make function with x=1 +Make function with x=2 +Make function with x=3 +x is 1 +x is 2 +x is 3 +-- End -- + +-- Testcase -- +{% + let count=0; + + function a() { + let x = ++count; + print("Make function with x=", x, "\n"); + return function() { + print("x is ", x, "\n"); + }; + } + + let fn1 = a(); + let fn2 = a(); + let fn3 = a(); + + fn1(); + fn2(); + fn3(); +%} +-- End -- diff --git a/tests/custom/02_runtime/06_recursion b/tests/custom/02_runtime/06_recursion new file mode 100644 index 0000000..470fc3a --- /dev/null +++ b/tests/custom/02_runtime/06_recursion @@ -0,0 +1,59 @@ +Testing recursive invocations. + + +1. Testing recursive invocation. + +-- Expect stdout -- +1 +2 +4 +8 +16 +32 +64 +128 +256 +512 +1024 +2048 +4096 +8192 +16384 +-- End -- + +-- Testcase -- +{% + function test(x) { + print(x, "\n"); + + if (x < 10000) + test(x * 2); + } + + test(1); +%} +-- End -- + + +2. Testing infinite recursion. + +-- Expect stderr -- +Runtime error: Too much recursion +In test(), line 3, byte 8: + called from anonymous function ([stdin]:6:7) + + ` test();` + Near here ---^ + + +-- End -- + +-- Testcase -- +{% + function test() { + test(); + } + + test(); +%} +-- End -- diff --git a/tests/custom/03_bugs/01_try_catch_stack_mismatch b/tests/custom/03_bugs/01_try_catch_stack_mismatch new file mode 100644 index 0000000..f6e5a0a --- /dev/null +++ b/tests/custom/03_bugs/01_try_catch_stack_mismatch @@ -0,0 +1,52 @@ +When compiling a try/catch statement with an exception variable, the catch +skip jump incorrectly pointed to the POP instruction popping the exception +variable off the stack, leading to a stack position mismatch between +compiler and vm, causing local variables to yield wrong values at runtime. + +-- Expect stdout -- +1 +-- End -- + +-- Testcase -- +{% + function f() { + let x; + + try { + x = 1; + } + catch(e) { + + } + + // Before the fix, `x` incorrectly yielded the print function value + print(x, "\n"); + } + + f() +%} +-- End -- + + +When compiling a try/catch statement with local variable declearations +within the try block, the catch skip jump incorrectly happened before the +local try block variables were popped off the stack, leading to a stack +position mismatch between compiler and vm, causing local variables to +yield wrong values at runtime. + +-- Expect stdout -- +1 +-- End -- + +-- Testcase -- +{% + try { + let a; + } + catch {} + + let b = 1; + + print(b, "\n"); +%} +-- End -- diff --git a/tests/custom/03_bugs/02_array_pop_use_after_free b/tests/custom/03_bugs/02_array_pop_use_after_free new file mode 100644 index 0000000..22f63ff --- /dev/null +++ b/tests/custom/03_bugs/02_array_pop_use_after_free @@ -0,0 +1,14 @@ +When popping an element off an array, especially the last one, the popped +value might have been freed before the refcount was increased later on +function return. + +-- Expect stdout -- +1 +-- End -- + +-- Testcase -- +{% + x = [1]; + print(pop(x), "\n"); // This caused a SIGABRT before the bugfix +%} +-- End -- diff --git a/tests/custom/03_bugs/03_switch_fallthrough_miscompilation b/tests/custom/03_bugs/03_switch_fallthrough_miscompilation new file mode 100644 index 0000000..3e6410e --- /dev/null +++ b/tests/custom/03_bugs/03_switch_fallthrough_miscompilation @@ -0,0 +1,16 @@ +When falling through from a matched switch case into the default case, +the compiler incorrectly emitted bytecode that led to an endless loop. + +-- Expect stdout -- +1 +-- End -- + +-- Testcase -- +{% + switch (1) { + case 1: + default: + print("1\n"); + } +%} +-- End -- diff --git a/tests/custom/03_bugs/04_property_set_abort b/tests/custom/03_bugs/04_property_set_abort new file mode 100644 index 0000000..8af477f --- /dev/null +++ b/tests/custom/03_bugs/04_property_set_abort @@ -0,0 +1,76 @@ +When attempting to set a property on a non-array, non-object value the +VM aborted due to an assert triggered by libjson-c. + +-- Testcase -- +{% (null).x = 1 %} +-- End -- + +-- Expect stderr -- +Type error: attempt to set property on null value +In line 1, byte 15: + + `{% (null).x = 1 %}` + Near here ----^ + + +-- End -- + + +-- Testcase -- +{% (1).x = 1 %} +-- End -- + +-- Expect stderr -- +Type error: attempt to set property on integer value +In line 1, byte 12: + + `{% (1).x = 1 %}` + Near here -^ + + +-- End -- + + +-- Testcase -- +{% (1.2).x = 1 %} +-- End -- + +-- Expect stderr -- +Type error: attempt to set property on double value +In line 1, byte 14: + + `{% (1.2).x = 1 %}` + Near here ---^ + + +-- End -- + + +-- Testcase -- +{% (true).x = 1 %} +-- End -- + +-- Expect stderr -- +Type error: attempt to set property on boolean value +In line 1, byte 15: + + `{% (true).x = 1 %}` + Near here ----^ + + +-- End -- + + +-- Testcase -- +{% ("test").x = 1 %} +-- End -- + +-- Expect stderr -- +Type error: attempt to set property on string value +In line 1, byte 17: + + `{% ("test").x = 1 %}` + Near here ------^ + + +-- End -- diff --git a/tests/custom/03_bugs/05_duplicate_ressource_type b/tests/custom/03_bugs/05_duplicate_ressource_type new file mode 100644 index 0000000..21166b2 --- /dev/null +++ b/tests/custom/03_bugs/05_duplicate_ressource_type @@ -0,0 +1,31 @@ +When requiring a C module that registers custom ressource types multiple +times, ressource values instantiated after subsequent requires of the +same extensions didn't properly function since the internal type prototype +was resolved to the initial copy and subsequently discarded due to an index +mismatch. + +-- Testcase -- +{% + fs = require("fs"); + fd = fs.open("README.md"); + + printf("fd.read() #1: %s\n", + fd.read("line") ? "working" : "not working (" + fd.error() + ")"); + + fd.close(); + + + fs = require("fs"); + fd = fs.open("README.md"); + + printf("fd.read() #2: %s\n", + fd.read("line") ? "working" : "not working (" + fd.error() + ")"); + + fd.close(); +%} +-- End -- + +-- Expect stdout -- +fd.read() #1: working +fd.read() #2: working +-- End -- diff --git a/tests/custom/03_bugs/06_lexer_escape_at_boundary b/tests/custom/03_bugs/06_lexer_escape_at_boundary new file mode 100644 index 0000000..e80b0a1 --- /dev/null +++ b/tests/custom/03_bugs/06_lexer_escape_at_boundary @@ -0,0 +1,12 @@ +When the lexer processed a backslash introducing a string escape directly +at the buffer boundary, the backslash was incorrectly retained. + +-- Testcase -- +{% + print("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl\n"); +%} +-- End -- + +-- Expect stdout -- +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl +-- End -- diff --git a/tests/custom/03_bugs/07_lexer_overlong_lines b/tests/custom/03_bugs/07_lexer_overlong_lines new file mode 100644 index 0000000..d2dd3be --- /dev/null +++ b/tests/custom/03_bugs/07_lexer_overlong_lines @@ -0,0 +1,13 @@ +A logic flaw in the lineinfo encoding function led to an endless tight +loop when a buffer chunk with 128 byte got consumed, which may happen +when parsing very long literals. + +-- Testcase -- +{% + print("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg\n"); +%} +-- End -- + +-- Expect stdout -- +abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg +-- End -- diff --git a/tests/custom/03_bugs/08_compiler_arrow_fn_expressions b/tests/custom/03_bugs/08_compiler_arrow_fn_expressions new file mode 100644 index 0000000..5cd8960 --- /dev/null +++ b/tests/custom/03_bugs/08_compiler_arrow_fn_expressions @@ -0,0 +1,15 @@ +Arrow functions with single expression bodies were parsed with a wrong +precedence level, causing comma expressions to be greedily consumed. + +-- Testcase -- +{% + print({ + a: () => 1, + b: () => 2 + }, "\n"); +%} +-- End -- + +-- Expect stdout -- +{ "a": "() => { ... }", "b": "() => { ... }" } +-- End -- diff --git a/tests/custom/03_bugs/09_reject_invalid_array_indexes b/tests/custom/03_bugs/09_reject_invalid_array_indexes new file mode 100644 index 0000000..a7e5272 --- /dev/null +++ b/tests/custom/03_bugs/09_reject_invalid_array_indexes @@ -0,0 +1,25 @@ +Since libjson-c's json_object_get_int64() returns 0 for any input value +that has no integer representation, any kind of invalid array index +incorrectly yielded the first array element. + +-- Testcase -- +{% + x = [1, 2, 3]; + + print([ + x[1], + x["1"], + x[1.0], + x[1.1], + x.foo, + x["foo"], + x["0abc"], + x[x], + x[{ foo: true }] + ], "\n"); +%} +-- End -- + +-- Expect stdout -- +[ 2, 2, 2, null, null, null, null, null, null ] +-- End -- diff --git a/tests/custom/03_bugs/10_break_stack_mismatch b/tests/custom/03_bugs/10_break_stack_mismatch new file mode 100644 index 0000000..ae16dac --- /dev/null +++ b/tests/custom/03_bugs/10_break_stack_mismatch @@ -0,0 +1,38 @@ +When emitting jump instructions for breaking out of for-loops, the compiler +incorrectly set the jump target before the pop instruction clearing the +intermediate loop variables. Since the break instruction itself already +compiles to a series of pop instructions reverting the stack to it's the +pre-loop state, intermediate values got popped twice, leading to a stack +layout mismatch between compiler and VM, resulting in wrong local variable +values or segmentation faults at runtime. + +-- Testcase -- +{% + let x = 1; + + for (let y in [2]) + break; + + print(x, "\n"); +%} +-- End -- + +-- Expect stdout -- +1 +-- End -- + + +-- Testcase -- +{% + let x = 1; + + for (let y = 0; y < 1; y++) + break; + + print(x, "\n"); +%} +-- End -- + +-- Expect stdout -- +1 +-- End -- diff --git a/tests/custom/03_bugs/11_switch_stack_mismatch b/tests/custom/03_bugs/11_switch_stack_mismatch new file mode 100644 index 0000000..cc3b41a --- /dev/null +++ b/tests/custom/03_bugs/11_switch_stack_mismatch @@ -0,0 +1,39 @@ +When jumping into a case following prior cases declaring local variables, +the preceding local variable declarations were skipped, leading to an +unexpected stack layout which caused local variables to carry wrong +values at run time and eventual segmentation faults when attempting to +unwind the stack on leaving the lexical switch scope. + +-- Expect stdout -- +Matching 1: + - 1: [ null, null, 3, 4 ] + - 2: [ null, null, 3, 4, 5, 6 ] +Matching 2: + - 2: [ null, null, null, null, 5, 6 ] +Matching 3: + - default: [ 1, 2 ] + - 1: [ 1, 2, 3, 4 ] + - 2: [ 1, 2, 3, 4, 5, 6 ] +-- End -- + +-- Testcase -- +{% + for (let n in [1, 2, 3]) { + printf("Matching %d:\n", n); + + switch (n) { + default: + let x = 1, y = 2; + print(" - default: ", [x, y], "\n"); + + case 1: + let a = 3, b = 4; + print(" - 1: ", [x, y, a, b], "\n"); + + case 2: + let c = 5, d = 6; + print(" - 2: ", [x, y, a, b, c, d], "\n"); + } + } +%} +-- End -- diff --git a/tests/custom/03_bugs/12_altblock_stack_mismatch b/tests/custom/03_bugs/12_altblock_stack_mismatch new file mode 100644 index 0000000..e350660 --- /dev/null +++ b/tests/custom/03_bugs/12_altblock_stack_mismatch @@ -0,0 +1,83 @@ +When compiling alternative syntax blocks, such as `for ...: endfor`, +`if ...: endif` etc., the compiler didn't assign the contained statements +to a dedicated lexical scope, which caused a stack mismatch between +compiler and vm when such blocks declaring local variables weren't +actually executed. + +-- Expect stdout -- +2 +-- End -- + +-- Testcase -- +{% + if (false): + let a = 1; + endif; + + /* Due to lack of own lexical scope above, the compiler assumed + * that `a` is still on stack but the code to initialize it was + * never executed, so stack offsets were shifted by one from here + * on throughout the rest of the program. */ + + let b = 2; + + print(b, "\n"); +%} +-- End -- + + +Test a variation of the bug using `for in..endfor` loop syntax. + +-- Expect stdout -- +2 +-- End -- + +-- Testcase -- +{% + for (let x in []): + let a = 1; + endfor; + + let b = 2; + + print(b, "\n"); +%} +-- End -- + + +Test a variation of the bug using `for..endfor` count loop syntax. + +-- Expect stdout -- +2 +-- End -- + +-- Testcase -- +{% + for (let i = 0; i < 0; i++): + let a = 1; + endfor; + + let b = 2; + + print(b, "\n"); +%} +-- End -- + + +Test a variation of the bug using `while..endwhile` loop syntax. + +-- Expect stdout -- +2 +-- End -- + +-- Testcase -- +{% + while (false): + let a = 1; + endwhile; + + let b = 2; + + print(b, "\n"); +%} +-- End -- diff --git a/tests/custom/CMakeLists.txt b/tests/custom/CMakeLists.txt new file mode 100644 index 0000000..9672fce --- /dev/null +++ b/tests/custom/CMakeLists.txt @@ -0,0 +1,7 @@ +ADD_TEST( + NAME custom + COMMAND run_tests.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +SET_PROPERTY(TEST custom APPEND PROPERTY ENVIRONMENT "UCODE_BIN=$") diff --git a/tests/custom/run_tests.sh b/tests/custom/run_tests.sh new file mode 100755 index 0000000..962cd9a --- /dev/null +++ b/tests/custom/run_tests.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash + +line='........................................' +ucode_bin=${UCODE_BIN:-./ucode} + +extract_sections() { + local file=$1 + local dir=$2 + local count=0 + local tag line outfile + + while IFS= read -r line; do + case "$line" in + "-- Testcase --") + tag="test" + count=$((count + 1)) + outfile=$(printf "%s/%03d.in" "$dir" $count) + printf "" > "$outfile" + ;; + "-- Expect stdout --"|"-- Expect stderr --"|"-- Expect exitcode --") + tag="${line#-- Expect }" + tag="${tag% --}" + count=$((count + 1)) + outfile=$(printf "%s/%03d.%s" "$dir" $count "$tag") + printf "" > "$outfile" + ;; + "-- End --") + tag="" + outfile="" + ;; + *) + if [ -n "$tag" ]; then + printf "%s\\n" "$line" >> "$outfile" + fi + ;; + esac + done < "$file" + + return $(ls -l "$dir/"*.in 2>/dev/null | wc -l) +} + +run_testcase() { + local num=$1 + local dir=$2 + local in=$3 + local out=$4 + local err=$5 + local code=$6 + local fail=0 + + $ucode_bin -e '{ "REQUIRE_SEARCH_PATH": [ "./lib/*.so" ] }' -i - <"$in" >"$dir/res.out" 2>"$dir/res.err" + + touch "$dir/empty" + printf "%d\n" $? > "$dir/res.code" + + if ! cmp -s "$dir/res.err" "${err:-$dir/empty}"; then + [ $fail = 0 ] && printf "!\n" + printf "Testcase #%d: Expected stderr did not match:\n" $num + diff -u --color=always --label="Expected stderr" --label="Resulting stderr" "${err:-$dir/empty}" "$dir/res.err" + printf -- "---\n" + fail=1 + fi + + if ! cmp -s "$dir/res.out" "${out:-$dir/empty}"; then + [ $fail = 0 ] && printf "!\n" + printf "Testcase #%d: Expected stdout did not match:\n" $num + diff -u --color=always --label="Expected stdout" --label="Resulting stdout" "${out:-$dir/empty}" "$dir/res.out" + printf -- "---\n" + fail=1 + fi + + if [ -n "$code" ] && ! cmp -s "$dir/res.code" "$code"; then + [ $fail = 0 ] && printf "!\n" + printf "Testcase #%d: Expected exit code did not match:\n" $num + diff -u --color=always --label="Expected code" --label="Resulting code" "$code" "$dir/res.code" + printf -- "---\n" + fail=1 + fi + + return $fail +} + +run_test() { + local file=$1 + local name=${file##*/} + local res ecode eout eerr ein tests + local testcase_first=0 failed=0 count=0 + + printf "%s %s " "$name" "${line:${#name}}" + + mkdir "/tmp/test.$$" + + extract_sections "$file" "/tmp/test.$$" + tests=$? + + [ -f "/tmp/test.$$/001.in" ] && testcase_first=1 + + for res in "/tmp/test.$$/"[0-9]*; do + case "$res" in + *.in) + count=$((count + 1)) + + if [ $testcase_first = 1 ]; then + # Flush previous test + if [ -n "$ein" ]; then + run_testcase $count "/tmp/test.$$" "$ein" "$eout" "$eerr" "$ecode" || failed=$((failed + 1)) + eout="" + eerr="" + ecode="" + fi + + ein=$res + else + run_testcase $count "/tmp/test.$$" "$res" "$eout" "$eerr" "$ecode" || failed=$((failed + 1)) + + eout="" + eerr="" + ecode="" + fi + + ;; + *.stdout) eout=$res ;; + *.stderr) eerr=$res ;; + *.exitcode) ecode=$res ;; + esac + done + + # Flush last test + if [ $testcase_first = 1 ] && [ -n "$eout$eerr$ecode" ]; then + run_testcase $count "/tmp/test.$$" "$ein" "$eout" "$eerr" "$ecode" || failed=$((failed + 1)) + fi + + rm -r "/tmp/test.$$" + + if [ $failed = 0 ]; then + printf "OK\n" + else + printf "%s %s FAILED (%d/%d)\n" "$name" "${line:${#name}}" $failed $tests + fi + + return $failed +} + + +n_tests=0 +n_fails=0 + +select_tests="$@" + +use_test() { + local input="$(readlink -f "$1")" + local test + + [ -f "$input" ] || return 1 + [ -n "$select_tests" ] || return 0 + + for test in "$select_tests"; do + test="$(readlink -f "$test")" + + [ "$test" != "$input" ] || return 0 + done + + return 1 +} + +for catdir in [0-9][0-9]_*; do + [ -d "$catdir" ] || continue + + printf "\n##\n## Running %s tests\n##\n\n" "${catdir##*/[0-9][0-9]_}" + + for testfile in "$catdir/"[0-9][0-9]_*; do + use_test "$testfile" || continue + + n_tests=$((n_tests + 1)) + run_test "$testfile" || n_fails=$((n_fails + 1)) + done +done + +printf "\nRan %d tests, %d okay, %d failures\n" $n_tests $((n_tests - n_fails)) $n_fails -- cgit v1.2.3 From 1c548a69413c4d5b701da398251b844f18f0dadd Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 17:01:30 +0100 Subject: cmake: do not output binaries into lib directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn't work properly with out of tree builds etc. Signed-off-by: Petr Štetiar --- CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53de0b4..c9eb31a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,13 +63,13 @@ SET(LIBRARIES "") IF(FS_SUPPORT) SET(LIBRARIES ${LIBRARIES} fs_lib) ADD_LIBRARY(fs_lib MODULE lib/fs.c) - SET_TARGET_PROPERTIES(fs_lib PROPERTIES OUTPUT_NAME lib/fs PREFIX "") + SET_TARGET_PROPERTIES(fs_lib PROPERTIES OUTPUT_NAME fs PREFIX "") ENDIF() IF(MATH_SUPPORT) SET(LIBRARIES ${LIBRARIES} math_lib) ADD_LIBRARY(math_lib MODULE lib/math.c) - SET_TARGET_PROPERTIES(math_lib PROPERTIES OUTPUT_NAME lib/math PREFIX "") + SET_TARGET_PROPERTIES(math_lib PROPERTIES OUTPUT_NAME math PREFIX "") CHECK_FUNCTION_EXISTS(ceil CEIL_FUNCTION_EXISTS) IF (NOT CEIL_FUNCTION_EXISTS) TARGET_LINK_LIBRARIES(math_lib m) @@ -83,7 +83,7 @@ IF(UBUS_SUPPORT) INCLUDE_DIRECTORIES(${ubus_include_dir}) SET(LIBRARIES ${LIBRARIES} ubus_lib) ADD_LIBRARY(ubus_lib MODULE lib/ubus.c) - SET_TARGET_PROPERTIES(ubus_lib PROPERTIES OUTPUT_NAME lib/ubus PREFIX "") + SET_TARGET_PROPERTIES(ubus_lib PROPERTIES OUTPUT_NAME ubus PREFIX "") TARGET_LINK_LIBRARIES(ubus_lib ${ubus} ${blobmsg_json}) ENDIF() @@ -93,9 +93,9 @@ IF(UCI_SUPPORT) INCLUDE_DIRECTORIES(${uci_include_dir}) SET(LIBRARIES ${LIBRARIES} uci_lib) ADD_LIBRARY(uci_lib MODULE lib/uci.c) - SET_TARGET_PROPERTIES(uci_lib PROPERTIES OUTPUT_NAME lib/uci PREFIX "") + SET_TARGET_PROPERTIES(uci_lib PROPERTIES OUTPUT_NAME uci PREFIX "") TARGET_LINK_LIBRARIES(uci_lib ${uci}) ENDIF() INSTALL(TARGETS ucode RUNTIME DESTINATION bin) -INSTALL(TARGETS ${LIBRARIES} LIBRARY DESTINATION lib/ucode) +INSTALL(TARGETS ${LIBRARIES} LIBRARY DESTINATION ucode) -- cgit v1.2.3 From 41d33d0b2b09efb7b3cddefa2793cf2133a7b5dc Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 17:02:55 +0100 Subject: tests: custom: return exit code if tests fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise tests always pass in ctest. Signed-off-by: Petr Štetiar --- tests/custom/run_tests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/custom/run_tests.sh b/tests/custom/run_tests.sh index 962cd9a..2364914 100755 --- a/tests/custom/run_tests.sh +++ b/tests/custom/run_tests.sh @@ -177,3 +177,4 @@ for catdir in [0-9][0-9]_*; do done printf "\nRan %d tests, %d okay, %d failures\n" $n_tests $((n_tests - n_fails)) $n_fails +exit $n_fails -- cgit v1.2.3 From df73b25de218833588a9770f6beb9e4a2908bcb7 Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 17:04:29 +0100 Subject: tests: add more tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add cram based tests * test under either valgrind or LLVM sanitizers * add libFuzzer template Signed-off-by: Petr Štetiar --- .gitignore | 1 + CMakeLists.txt | 19 +++++++++++++-- tests/CMakeLists.txt | 6 +++++ tests/cram/CMakeLists.txt | 27 +++++++++++++++++++++ tests/cram/test_basic.t | 58 +++++++++++++++++++++++++++++++++++++++++++++ tests/custom/CMakeLists.txt | 11 ++++++++- tests/fuzz/CMakeLists.txt | 16 +++++++++++++ tests/fuzz/corpus/.keep | 0 tests/fuzz/test-fuzz.c | 9 +++++++ 9 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/cram/CMakeLists.txt create mode 100644 tests/cram/test_basic.t create mode 100644 tests/fuzz/CMakeLists.txt create mode 100644 tests/fuzz/corpus/.keep create mode 100644 tests/fuzz/test-fuzz.c diff --git a/.gitignore b/.gitignore index cb1f574..e111836 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ parser.out contrib/lemon utpl lib/*.so +tests/cram/*.t.err diff --git a/CMakeLists.txt b/CMakeLists.txt index c9eb31a..88ee376 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,8 @@ IF(JSONC_FOUND) INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS}) ENDIF() -ADD_EXECUTABLE(ucode main.c lexer.c lib.c vm.c chunk.c value.c object.c compiler.c source.c) +SET(UCODE_SOURCES main.c lexer.c lib.c vm.c chunk.c value.c object.c compiler.c source.c) +ADD_EXECUTABLE(ucode ${UCODE_SOURCES}) TARGET_LINK_LIBRARIES(ucode ${json}) CHECK_FUNCTION_EXISTS(dlopen DLOPEN_FUNCTION_EXISTS) @@ -89,12 +90,26 @@ ENDIF() IF(UCI_SUPPORT) FIND_LIBRARY(uci NAMES uci) + FIND_LIBRARY(ubox NAMES ubox) FIND_PATH(uci_include_dir uci.h) INCLUDE_DIRECTORIES(${uci_include_dir}) SET(LIBRARIES ${LIBRARIES} uci_lib) ADD_LIBRARY(uci_lib MODULE lib/uci.c) SET_TARGET_PROPERTIES(uci_lib PROPERTIES OUTPUT_NAME uci PREFIX "") - TARGET_LINK_LIBRARIES(uci_lib ${uci}) + TARGET_LINK_LIBRARIES(uci_lib ${uci} ${ubox}) +ENDIF() + +IF(UNIT_TESTING) + ENABLE_TESTING() + ADD_DEFINITIONS(-DUNIT_TESTING) + ADD_SUBDIRECTORY(tests) + + IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") + ADD_EXECUTABLE(ucode-san ${UCODE_SOURCES}) + TARGET_LINK_LIBRARIES(ucode-san ${json}) + TARGET_COMPILE_OPTIONS(ucode-san PRIVATE -g -fno-omit-frame-pointer -fsanitize=undefined,address,leak -fno-sanitize-recover=all) + TARGET_LINK_OPTIONS(ucode-san PRIVATE -fsanitize=undefined,address,leak) + ENDIF() ENDIF() INSTALL(TARGETS ucode RUNTIME DESTINATION bin) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..aed0100 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,6 @@ +ADD_SUBDIRECTORY(cram) +ADD_SUBDIRECTORY(custom) + +IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") + ADD_SUBDIRECTORY(fuzz) +ENDIF() diff --git a/tests/cram/CMakeLists.txt b/tests/cram/CMakeLists.txt new file mode 100644 index 0000000..a93add5 --- /dev/null +++ b/tests/cram/CMakeLists.txt @@ -0,0 +1,27 @@ +FIND_PACKAGE(PythonInterp 3 REQUIRED) +FILE(GLOB test_cases "test_*.t") + +SET(PYTHON_VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/.venv") +SET(PYTHON_VENV_PIP "${PYTHON_VENV_DIR}/bin/pip") +SET(PYTHON_VENV_CRAM "${PYTHON_VENV_DIR}/bin/cram") + +ADD_CUSTOM_COMMAND( + OUTPUT ${PYTHON_VENV_CRAM} + COMMAND ${PYTHON_EXECUTABLE} -m venv ${PYTHON_VENV_DIR} + COMMAND ${PYTHON_VENV_PIP} install cram +) +ADD_CUSTOM_TARGET(prepare-cram-venv ALL DEPENDS ${PYTHON_VENV_CRAM}) + +ADD_TEST( + NAME cram + COMMAND ${PYTHON_VENV_CRAM} ${test_cases} ${test_cases_san} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +SET_PROPERTY(TEST cram APPEND PROPERTY ENVIRONMENT "BUILD_BIN_DIR=$") + +IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") + SET_PROPERTY(TEST cram APPEND PROPERTY ENVIRONMENT "UCODE_BIN=$") +ELSE() + SET_PROPERTY(TEST cram APPEND PROPERTY ENVIRONMENT "UCODE_BIN=valgrind --quiet --leak-check=full $") +ENDIF() diff --git a/tests/cram/test_basic.t b/tests/cram/test_basic.t new file mode 100644 index 0000000..5061d37 --- /dev/null +++ b/tests/cram/test_basic.t @@ -0,0 +1,58 @@ +setup common environment: + + $ [ -n "$BUILD_BIN_DIR" ] && export PATH="$BUILD_BIN_DIR:$PATH" + $ alias ucode="$UCODE_BIN" + + $ for m in $BUILD_BIN_DIR/*.so; do + > ln -s "$m" "$(pwd)/$(basename $m)"; \ + > done + +check that ucode provides exepected help: + + $ ucode | sed 's/ucode-san/ucode/' + == Usage == + + # ucode [-d] [-l] [-r] [-S] [-e '[prefix=]{"var": ...}'] [-E [prefix=]env.json] {-i | -s "ucode script..."} + -h, --help\tPrint this help (esc) + -i file\tSpecify an ucode script to parse (esc) + -s "ucode script..."\tSpecify an ucode fragment to parse (esc) + -d Instead of executing the script, dump the resulting AST as dot + -l Do not strip leading block whitespace + -r Do not trim trailing block newlines + -S Enable strict mode + -e Set global variables from given JSON object + -E Set global variables from given JSON file + -m Preload given module + +check that ucode prints greetings: + + $ ucode -s "{% print('hello world') %}" + hello world (no-eol) + +check that ucode provides proper error messages: + + $ ucode -m foo + One of -i or -s is required + [1] + + $ ucode -m foo -s '' + Runtime error: No module named 'foo' could be found + At offset 0 + + [1] + + $ touch moo; ucode -m foo -i moo + Runtime error: No module named 'foo' could be found + At offset 0 + + [1] + +check that ucode can load fs module: + + $ ucode -m fs + One of -i or -s is required + [1] + + $ ucode -m fs -s '' + + $ touch moo; ucode -m fs -i moo diff --git a/tests/custom/CMakeLists.txt b/tests/custom/CMakeLists.txt index 9672fce..d56cb99 100644 --- a/tests/custom/CMakeLists.txt +++ b/tests/custom/CMakeLists.txt @@ -3,5 +3,14 @@ ADD_TEST( COMMAND run_tests.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) +SET_PROPERTY(TEST custom APPEND PROPERTY ENVIRONMENT "UCODE_BIN=valgrind --quiet --leak-check=full $") -SET_PROPERTY(TEST custom APPEND PROPERTY ENVIRONMENT "UCODE_BIN=$") +IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") + ADD_TEST( + NAME custom-san + COMMAND run_tests.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + + SET_PROPERTY(TEST custom-san APPEND PROPERTY ENVIRONMENT "UCODE_BIN=$") +ENDIF() diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt new file mode 100644 index 0000000..384e7e0 --- /dev/null +++ b/tests/fuzz/CMakeLists.txt @@ -0,0 +1,16 @@ +MACRO(ADD_FUZZER_TEST name) + ADD_EXECUTABLE(${name} ${name}.c) + TARGET_COMPILE_OPTIONS(${name} PRIVATE -g -O1 -fno-omit-frame-pointer -fsanitize=fuzzer,address,leak,undefined) + TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR}) + TARGET_LINK_OPTIONS(${name} PRIVATE -stdlib=libc++ -fsanitize=fuzzer,address,leak,undefined) + ADD_TEST( + NAME ${name} + COMMAND ${name} -max_len=256 -timeout=10 -max_total_time=300 ${CMAKE_CURRENT_SOURCE_DIR}/corpus + ) +ENDMACRO(ADD_FUZZER_TEST) + +FILE(GLOB test_cases "test-*.c") +FOREACH(test_case ${test_cases}) + GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE) + ADD_FUZZER_TEST(${test_case}) +ENDFOREACH(test_case) diff --git a/tests/fuzz/corpus/.keep b/tests/fuzz/corpus/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tests/fuzz/test-fuzz.c b/tests/fuzz/test-fuzz.c new file mode 100644 index 0000000..40649e2 --- /dev/null +++ b/tests/fuzz/test-fuzz.c @@ -0,0 +1,9 @@ +#include +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t size) +{ + return 0; +} -- cgit v1.2.3 From 0e4a3872ab466e715c84a379a53dfce9f4f9aebc Mon Sep 17 00:00:00 2001 From: Petr Štetiar Date: Fri, 19 Mar 2021 17:08:23 +0100 Subject: Add initial GitLab and GitHub CI support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uses currently proof-of-concept openwrt-ci[1] in order to: * improve the quality of the codebase in various areas * decrease code review time and help merging contributions faster * get automagic feedback loop on various platforms and tools - out of tree build with OpenWrt SDK on following targets: * ath79-generic * imx6-generic * malta-be * mvebu-cortexa53 - out of tree native build on x86/64 with GCC (versions 8, 9, 10) and Clang 11 - out of tree native x86/64 static code analysis with cppcheck and scan-build from Clang 11 1. https://gitlab.com/ynezz/openwrt-ci/ Signed-off-by: Petr Štetiar --- .github/workflows/openwrt-ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++ .gitlab-ci.yml | 7 ++++++ 2 files changed, 55 insertions(+) create mode 100644 .github/workflows/openwrt-ci.yml create mode 100644 .gitlab-ci.yml diff --git a/.github/workflows/openwrt-ci.yml b/.github/workflows/openwrt-ci.yml new file mode 100644 index 0000000..08a9ccb --- /dev/null +++ b/.github/workflows/openwrt-ci.yml @@ -0,0 +1,48 @@ +name: OpenWrt CI testing + +on: [ push, pull_request ] +env: + CI_ENABLE_UNIT_TESTING: 1 + CI_TARGET_BUILD_DEPENDS: ubus uci + +jobs: + native_testing: + name: Various native checks + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - uses: ynezz/gh-actions-openwrt-ci-native@v0.0.1 + + - name: Upload build artifacts + uses: actions/upload-artifact@v2 + if: failure() + with: + name: native-build-artifacts + if-no-files-found: ignore + path: | + build/scan + tests/cram/**/*.t.err + + sdk_build: + name: Build with OpenWrt ${{ matrix.sdk_platform }} SDK (out of tree) + runs-on: ubuntu-20.04 + + strategy: + fail-fast: false + matrix: + sdk_platform: + - ath79-generic + - imx6-generic + - malta-be + - mvebu-cortexa53 + + steps: + - uses: actions/checkout@v2 + + - name: Out of tree build with OpenWrt ${{ matrix.sdk_platform }} SDK + uses: ynezz/gh-actions-openwrt-ci-sdk@v0.0.1 + env: + CI_TARGET_SDK_RELEASE: master + CI_TARGET_SDK_IMAGE: ${{ matrix.sdk_platform }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..5ec04e4 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,7 @@ +variables: + CI_ENABLE_UNIT_TESTING: 1 + CI_TARGET_BUILD_DEPENDS: ubus uci + +include: + - remote: https://gitlab.com/ynezz/openwrt-ci/raw/master/openwrt-ci/gitlab/main.yml + - remote: https://gitlab.com/ynezz/openwrt-ci/raw/master/openwrt-ci/gitlab/pipeline.yml -- cgit v1.2.3 From 93ededbe93ff1e5949387996b69892c9220a94e8 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 1 Apr 2021 19:24:31 +0200 Subject: tests: allow executing run_tests.sh from any directory Signed-off-by: Jo-Philipp Wich --- tests/custom/run_tests.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/custom/run_tests.sh b/tests/custom/run_tests.sh index 2364914..b20404a 100755 --- a/tests/custom/run_tests.sh +++ b/tests/custom/run_tests.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash +testdir=$(dirname "$0") +topdir=$(readlink -f "$testdir/../..") + line='........................................' -ucode_bin=${UCODE_BIN:-./ucode} +ucode_bin=${UCODE_BIN:-"$topdir/ucode"} extract_sections() { local file=$1 @@ -48,7 +51,10 @@ run_testcase() { local code=$6 local fail=0 - $ucode_bin -e '{ "REQUIRE_SEARCH_PATH": [ "./lib/*.so" ] }' -i - <"$in" >"$dir/res.out" 2>"$dir/res.err" + ( + cd "$topdir" + $ucode_bin -e '{ "REQUIRE_SEARCH_PATH": [ "'"$topdir"'/*.so" ] }' -i - <"$in" >"$dir/res.out" 2>"$dir/res.err" + ) touch "$dir/empty" printf "%d\n" $? > "$dir/res.code" @@ -163,7 +169,7 @@ use_test() { return 1 } -for catdir in [0-9][0-9]_*; do +for catdir in "$testdir/"[0-9][0-9]_*; do [ -d "$catdir" ] || continue printf "\n##\n## Running %s tests\n##\n\n" "${catdir##*/[0-9][0-9]_}" -- cgit v1.2.3 From f2c4b79feaffd7b2fdb4041f47c9cd0f4cc3bc6e Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 1 Apr 2021 19:24:49 +0200 Subject: treewide: fix issues reported by clang code analyzer Signed-off-by: Jo-Philipp Wich --- compiler.c | 24 +++++++++++++----------- lexer.c | 2 +- lib.c | 53 +++++++++++++++++++++++++++++++++++------------------ lib/fs.c | 1 + main.c | 2 +- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/compiler.c b/compiler.c index 7c0a2a1..757ff0c 100644 --- a/compiler.c +++ b/compiler.c @@ -1757,7 +1757,6 @@ uc_compiler_compile_object(uc_compiler *compiler, bool assignable) if (len > 0) { uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, I_SOBJ); uc_compiler_emit_u32(compiler, 0, len); - len = 0; } /* set initial size hint */ @@ -2166,14 +2165,14 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var) /* Initializer ---------------------------------------------------------- */ - /* We parsed a `local x` or `local x, y` expression, so (re)declare - * last label as local initializer variable */ - if (local) - uc_compiler_declare_local_null(compiler, var->pos, var->val); - - /* If we parsed at least on label, try continue parsing as variable + /* If we parsed at least one label, try continue parsing as variable * expression... */ if (var) { + /* We parsed a `local x` or `local x, y` expression, so (re)declare + * last label as local initializer variable */ + if (local) + uc_compiler_declare_local_null(compiler, var->pos, var->val); + uc_compiler_compile_labelexpr(compiler, true); uc_compiler_emit_insn(compiler, 0, I_POP); @@ -2268,10 +2267,6 @@ uc_compiler_compile_for(uc_compiler *compiler) local = uc_compiler_parse_match(compiler, TK_LOCAL); - if (local && !uc_compiler_parse_check(compiler, TK_LABEL)) - uc_compiler_syntax_error(compiler, compiler->parser->curr.pos, - "Expecting label after 'local'"); - if (uc_compiler_parse_match(compiler, TK_LABEL)) { keyvar = compiler->parser->prev; uc_value_get(keyvar.val); @@ -2291,6 +2286,12 @@ uc_compiler_compile_for(uc_compiler *compiler) goto out; } } + else if (local) { + uc_compiler_syntax_error(compiler, compiler->parser->curr.pos, + "Expecting label after 'local'"); + + goto out; + } /* * The previous expression ruled out a for-in loop, so continue parsing @@ -2335,6 +2336,7 @@ uc_compiler_compile_switch(uc_compiler *compiler) /* handle `default:` */ if (uc_compiler_parse_match(compiler, TK_DEFAULT)) { if (default_off) { + uc_vector_clear(&cases); uc_compiler_syntax_error(compiler, compiler->parser->prev.pos, "more than one switch default case"); diff --git a/lexer.c b/lexer.c index ada8671..1fba88f 100644 --- a/lexer.c +++ b/lexer.c @@ -728,7 +728,7 @@ parse_label(uc_lexer *lex, bool no_regexp) if (!buf_remaining(lex) || (lex->bufstart[0] != '_' && !isalnum(lex->bufstart[0]))) { for (i = 0, word = &reserved_words[0]; i < ARRAY_SIZE(reserved_words); i++, word = &reserved_words[i]) { - if (lex->lookbehindlen == word->plen && !strncmp(lex->lookbehind, word->pat, word->plen)) { + if (lex->lookbehind && lex->lookbehindlen == word->plen && !strncmp(lex->lookbehind, word->pat, word->plen)) { lookbehind_reset(lex); switch (word->type) { diff --git a/lib.c b/lib.c index a4cfdea..ea87a6b 100644 --- a/lib.c +++ b/lib.c @@ -508,6 +508,7 @@ uc_unshift(uc_vm *vm, size_t nargs) static json_object * uc_chr(uc_vm *vm, size_t nargs) { + json_object *rv = NULL; size_t idx; int64_t n; char *str; @@ -528,7 +529,10 @@ uc_chr(uc_vm *vm, size_t nargs) str[idx] = (char)n; } - return xjs_new_string_len(str, nargs); + rv = xjs_new_string_len(str, nargs); + free(str); + + return rv; } static json_object * @@ -1194,6 +1198,7 @@ uc_uc(uc_vm *vm, size_t nargs) static json_object * uc_uchr(uc_vm *vm, size_t nargs) { + json_object *rv = NULL; size_t idx, ulen; char *p, *str; int64_t n; @@ -1226,7 +1231,10 @@ uc_uchr(uc_vm *vm, size_t nargs) break; } - return xjs_new_string_len(str, ulen); + rv = xjs_new_string_len(str, ulen); + free(str); + + return rv; } static json_object * @@ -1663,11 +1671,10 @@ invalid: static json_object * uc_require(uc_vm *vm, size_t nargs) { - const char *name = json_object_get_string(uc_get_arg(0)); - json_object *val = uc_get_arg(0); json_object *search, *se, *res; size_t arridx, arrlen; + const char *name; if (!json_object_is_type(val, json_type_string)) return NULL; @@ -1882,8 +1889,7 @@ uc_replace_str(uc_vm *vm, json_object *str, const char *subject, regmatch_t *pmatch, size_t plen, char **sp, size_t *sl) { - const char *r = str ? json_object_get_string(str) : "null"; - const char *p = r; + const char *p, *r = str ? json_object_get_string(str) : "null"; bool esc = false; int i; @@ -2202,13 +2208,20 @@ uc_system(uc_vm *vm, size_t nargs) { json_object *cmdline = uc_get_arg(0); json_object *timeout = uc_get_arg(1); + const char **arglist, *fn, *s; sigset_t sigmask, sigomask; - const char **arglist, *fn; struct timespec ts; int64_t tms; + int rc, len; pid_t cld; size_t i; - int rc; + + if (timeout && (!json_object_is_type(timeout, json_type_int) || json_object_get_int64(timeout) < 0)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Invalid timeout specified"); + + return NULL; + } switch (json_object_get_type(cmdline)) { case json_type_string: @@ -2220,10 +2233,21 @@ uc_system(uc_vm *vm, size_t nargs) break; case json_type_array: - arglist = xalloc(sizeof(*arglist) * (json_object_array_length(cmdline) + 1)); + len = json_object_array_length(cmdline); + + if (len == 0) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Passed command array is empty"); - for (i = 0; i < json_object_array_length(cmdline); i++) - arglist[i] = json_object_get_string(json_object_array_get_idx(cmdline, i)); + return NULL; + } + + arglist = xalloc(sizeof(*arglist) * (len + 1)); + + for (i = 0; i < len; i++) { + s = json_object_get_string(json_object_array_get_idx(cmdline, i)); + arglist[i] = s ? s : "null"; + } arglist[i] = NULL; @@ -2236,13 +2260,6 @@ uc_system(uc_vm *vm, size_t nargs) return NULL; } - if (timeout && (!json_object_is_type(timeout, json_type_int) || json_object_get_int64(timeout) < 0)) { - uc_vm_raise_exception(vm, EXCEPTION_TYPE, - "Invalid timeout specified"); - - return NULL; - } - tms = timeout ? json_object_get_int64(timeout) : 0; if (tms > 0) { diff --git a/lib/fs.c b/lib/fs.c index 6ea4ec1..7a33529 100644 --- a/lib/fs.c +++ b/lib/fs.c @@ -629,6 +629,7 @@ uc_fs_getcwd(uc_vm *vm, size_t nargs) if (errno == ERANGE) continue; + free(buf); err_return(errno); } while (true); diff --git a/main.c b/main.c index 55c9dd8..c185524 100644 --- a/main.c +++ b/main.c @@ -197,7 +197,7 @@ static json_object * parse_envfile(FILE *fp) { json_object *rv = NULL; - enum json_tokener_error err; + enum json_tokener_error err = json_tokener_continue; struct json_tokener *tok; char buf[128]; size_t rlen; -- cgit v1.2.3 From 35af4ba4fc21a4b2357c50e6b02a2e3e4b236e88 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 21 Apr 2021 15:07:16 +0200 Subject: treewide: rework internal data type system Instead of relying on json_object values internally, use custom types to represent the different ucode value types which brings a number of advantages compared to the previous approach: - Due to the use of tagged pointers, small integer, string and bool values can be stored directly in the pointer addresses, vastly reducing required heap memory - Ability to create circular data structures such as `let o; o = { test: o };` - Ability to register custom `tostring()` function through prototypes - Initial mark/sweep GC implementation to tear down circular object graphs on VM deinit The change also paves the way for possible future extensions such as constant variables and meta methods for custom ressource types. Signed-off-by: Jo-Philipp Wich --- CMakeLists.txt | 5 +- chunk.c | 11 +- chunk.h | 10 +- compiler.c | 261 ++--- compiler.h | 12 +- lexer.c | 55 +- lexer.h | 5 +- lib.c | 1427 +++++++++++------------- lib.h | 151 +-- lib/fs.c | 312 +++--- lib/math.c | 66 +- lib/ubus.c | 127 ++- lib/uci.c | 431 ++++---- main.c | 77 +- module.h | 15 +- object.c | 493 --------- object.h | 201 ---- tests/custom/00_syntax/21_regex_literals | 2 +- types.c | 1755 ++++++++++++++++++++++++++++++ types.h | 310 ++++++ util.h | 11 + value.c | 345 +++--- value.h | 35 +- vm.c | 1039 +++++++++--------- vm.h | 25 +- 25 files changed, 4174 insertions(+), 3007 deletions(-) delete mode 100644 object.c delete mode 100644 object.h create mode 100644 types.c create mode 100644 types.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 88ee376..52d6262 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,8 +38,9 @@ IF(JSONC_FOUND) INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS}) ENDIF() -SET(UCODE_SOURCES main.c lexer.c lib.c vm.c chunk.c value.c object.c compiler.c source.c) +SET(UCODE_SOURCES main.c lexer.c lib.c vm.c chunk.c value.c compiler.c source.c types.c) ADD_EXECUTABLE(ucode ${UCODE_SOURCES}) +SET_PROPERTY(TARGET ucode PROPERTY ENABLE_EXPORTS 1) TARGET_LINK_LIBRARIES(ucode ${json}) CHECK_FUNCTION_EXISTS(dlopen DLOPEN_FUNCTION_EXISTS) @@ -113,4 +114,4 @@ IF(UNIT_TESTING) ENDIF() INSTALL(TARGETS ucode RUNTIME DESTINATION bin) -INSTALL(TARGETS ${LIBRARIES} LIBRARY DESTINATION ucode) +INSTALL(TARGETS ${LIBRARIES} LIBRARY DESTINATION lib/ucode) diff --git a/chunk.c b/chunk.c index 66c24af..7d3e498 100644 --- a/chunk.c +++ b/chunk.c @@ -17,6 +17,7 @@ #include #include "chunk.h" +#include "types.h" #include "util.h" #define OFFSETINFO_BITS (sizeof(((uc_offsetinfo *)NULL)->entries[0]) * 8) @@ -135,14 +136,14 @@ uc_chunk_pop(uc_chunk *chunk) } } -struct json_object * +uc_value_t * uc_chunk_get_constant(uc_chunk *chunk, size_t idx) { return uc_vallist_get(&chunk->constants, idx); } ssize_t -uc_chunk_add_constant(uc_chunk *chunk, struct json_object *val) +uc_chunk_add_constant(uc_chunk *chunk, uc_value_t *val) { return uc_vallist_add(&chunk->constants, val); } @@ -165,7 +166,7 @@ uc_chunk_debug_get_srcpos(uc_chunk *chunk, size_t off) } void -uc_chunk_debug_add_variable(uc_chunk *chunk, size_t from, size_t to, size_t slot, bool upval, json_object *name) +uc_chunk_debug_add_variable(uc_chunk *chunk, size_t from, size_t to, size_t slot, bool upval, uc_value_t *name) { uc_variables *variables = &chunk->debuginfo.variables; uc_value_list *varnames = &chunk->debuginfo.varnames; @@ -185,12 +186,12 @@ uc_chunk_debug_add_variable(uc_chunk *chunk, size_t from, size_t to, size_t slot variables->count++; } -json_object * +uc_value_t * uc_chunk_debug_get_variable(uc_chunk *chunk, size_t off, size_t slot, bool upval) { uc_variables *variables = &chunk->debuginfo.variables; uc_value_list *varnames = &chunk->debuginfo.varnames; - json_object *name = NULL; + uc_value_t *name = NULL; size_t i; assert(slot <= ((size_t)-1 / 2)); diff --git a/chunk.h b/chunk.h index a3eed0e..cad00f7 100644 --- a/chunk.h +++ b/chunk.h @@ -52,12 +52,14 @@ void uc_chunk_init(uc_chunk *chunk); void uc_chunk_free(uc_chunk *chunk); size_t uc_chunk_add(uc_chunk *chunk, uint8_t byte, size_t line); -ssize_t uc_chunk_add_constant(uc_chunk *chunk, struct json_object *value); -struct json_object *uc_chunk_get_constant(uc_chunk *chunk, size_t idx); +typedef struct uc_value_t uc_value_t; + +ssize_t uc_chunk_add_constant(uc_chunk *chunk, uc_value_t *value); +uc_value_t *uc_chunk_get_constant(uc_chunk *chunk, size_t idx); void uc_chunk_pop(uc_chunk *chunk); size_t uc_chunk_debug_get_srcpos(uc_chunk *chunk, size_t off); -void uc_chunk_debug_add_variable(uc_chunk *chunk, size_t from, size_t to, size_t slot, bool upval, json_object *name); -json_object *uc_chunk_debug_get_variable(uc_chunk *chunk, size_t off, size_t slot, bool upval); +void uc_chunk_debug_add_variable(uc_chunk *chunk, size_t from, size_t to, size_t slot, bool upval, uc_value_t *name); +uc_value_t *uc_chunk_debug_get_variable(uc_chunk *chunk, size_t off, size_t slot, bool upval); #endif /* __CHUNK_H_ */ diff --git a/compiler.c b/compiler.c index 757ff0c..04ff8ee 100644 --- a/compiler.c +++ b/compiler.c @@ -88,7 +88,7 @@ uc_compiler_parse_rules[TK_ERROR + 1] = { }; static ssize_t -uc_compiler_declare_local(uc_compiler *compiler, json_object *name); +uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name); static ssize_t uc_compiler_initialize_local(uc_compiler *compiler); @@ -96,11 +96,11 @@ uc_compiler_initialize_local(uc_compiler *compiler); static void uc_compiler_init(uc_compiler *compiler, const char *name, size_t srcpos, uc_source *source) { - json_object *varname = xjs_new_string("(callee)"); + uc_value_t *varname = ucv_string_new("(callee)"); compiler->scope_depth = 0; - compiler->function = uc_function_new(name, srcpos, source); + compiler->function = ucv_function_new(name, srcpos, source); compiler->locals.count = 0; compiler->locals.entries = NULL; @@ -117,69 +117,65 @@ uc_compiler_init(uc_compiler *compiler, const char *name, size_t srcpos, uc_sour /* reserve stack slot 0 */ uc_compiler_declare_local(compiler, varname); uc_compiler_initialize_local(compiler); - uc_value_put(varname); + ucv_put(varname); } static uc_chunk * uc_compiler_current_chunk(uc_compiler *compiler) { - return &compiler->function->chunk; + uc_function_t *fn = (uc_function_t *)compiler->function; + + return &fn->chunk; +} + +static uc_source * +uc_compiler_current_source(uc_compiler *compiler) +{ + uc_function_t *fn = (uc_function_t *)compiler->function; + + return fn->source; } __attribute__((format(printf, 3, 0))) static void uc_compiler_syntax_error(uc_compiler *compiler, size_t off, const char *fmt, ...) { + uc_stringbuf_t *buf = compiler->parser->error; size_t line = 0, byte = 0, len = 0; - char *context = NULL; - char *s, *tmp; va_list ap; + char *s; if (compiler->parser->synchronizing) return; compiler->parser->synchronizing = true; + if (!buf) + buf = compiler->parser->error = xprintbuf_new(); + if (!off) - off = uc_function_get_srcpos(compiler->function, + off = ucv_function_srcpos(compiler->function, uc_compiler_current_chunk(compiler)->count); if (off) { byte = off; - line = uc_source_get_line(compiler->function->source, &byte); - - format_error_context(&context, &len, compiler->function->source, NULL, off); + line = uc_source_get_line(uc_compiler_current_source(compiler), &byte); } va_start(ap, fmt); - xvasprintf(&s, fmt, ap); + len = xvasprintf(&s, fmt, ap); va_end(ap); - xasprintf(&tmp, "Syntax error: %s\n", s); - free(s); - s = tmp; + ucv_stringbuf_append(buf, "Syntax error: "); + ucv_stringbuf_addstr(buf, s, len); + ucv_stringbuf_append(buf, "\n"); - if (line) { - xasprintf(&tmp, "%sIn line %zu, byte %zu:\n", s, line, byte); - free(s); - s = tmp; - } + free(s); - if (context) { - xasprintf(&tmp, "%s%s\n\n", s, context); - free(context); - free(s); - s = tmp; - } + if (line) + ucv_stringbuf_printf(buf, "In line %zu, byte %zu:\n", line, byte); - if (compiler->parser->error) { - xasprintf(&tmp, "%s%s", compiler->parser->error, s); - free(compiler->parser->error); - free(s); - compiler->parser->error = tmp; - } - else { - compiler->parser->error = s; - } + if (format_error_context(buf, uc_compiler_current_source(compiler), NULL, off)) + ucv_stringbuf_append(buf, "\n\n"); } static size_t @@ -201,7 +197,7 @@ uc_compiler_parse_advance(uc_compiler *compiler) { bool no_regexp; - uc_value_put(compiler->parser->prev.val); + ucv_put(compiler->parser->prev.uv); compiler->parser->prev = compiler->parser->curr; while (true) { @@ -262,10 +258,10 @@ uc_compiler_parse_advance(uc_compiler *compiler) break; uc_compiler_syntax_error(compiler, compiler->parser->curr.pos, "%s", - json_object_get_string(compiler->parser->curr.val)); + ucv_string_get(compiler->parser->curr.uv)); - uc_value_put(compiler->parser->curr.val); - compiler->parser->curr.val = NULL; + ucv_put(compiler->parser->curr.uv); + compiler->parser->curr.uv = NULL; } } @@ -505,7 +501,7 @@ uc_compiler_set_u32(uc_compiler *compiler, size_t off, uint32_t n) } static size_t -uc_compiler_emit_constant(uc_compiler *compiler, size_t srcpos, json_object *val) +uc_compiler_emit_constant(uc_compiler *compiler, size_t srcpos, uc_value_t *val) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t cidx = uc_chunk_add_constant(chunk, val); @@ -517,7 +513,7 @@ uc_compiler_emit_constant(uc_compiler *compiler, size_t srcpos, json_object *val } static size_t -uc_compiler_emit_regexp(uc_compiler *compiler, size_t srcpos, json_object *val) +uc_compiler_emit_regexp(uc_compiler *compiler, size_t srcpos, uc_value_t *val) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t cidx = uc_chunk_add_constant(chunk, val); @@ -581,7 +577,7 @@ uc_compiler_set_jmpaddr(uc_compiler *compiler, size_t off, uint32_t dest) chunk->entries[off + 4] = addr % 0x100; } -static uc_function * +static uc_function_t * uc_compiler_finish(uc_compiler *compiler) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); @@ -600,7 +596,7 @@ uc_compiler_finish(uc_compiler *compiler) false, locals->entries[i].name); - uc_value_put(locals->entries[i].name); + ucv_put(locals->entries[i].name); } for (i = 0; i < upvals->count; i++) { @@ -611,19 +607,19 @@ uc_compiler_finish(uc_compiler *compiler) true, upvals->entries[i].name); - uc_value_put(upvals->entries[i].name); + ucv_put(upvals->entries[i].name); } uc_vector_clear(locals); uc_vector_clear(upvals); if (compiler->parser->error) { - uc_value_put(compiler->function->header.jso); + ucv_put(compiler->function); return NULL; } - return compiler->function; + return (uc_function_t *)compiler->function; } static void @@ -640,7 +636,7 @@ uc_compiler_leave_scope(uc_compiler *compiler) compiler->scope_depth--; - while (locals->count > 0 && locals->entries[locals->count - 1].depth > compiler->scope_depth) { + while (locals->count > 0 && locals->entries[locals->count - 1].depth > (ssize_t)compiler->scope_depth) { locals->count--; uc_chunk_debug_add_variable(chunk, @@ -650,7 +646,7 @@ uc_compiler_leave_scope(uc_compiler *compiler) false, locals->entries[locals->count].name); - uc_value_put(locals->entries[locals->count].name); + ucv_put(locals->entries[locals->count].name); locals->entries[locals->count].name = NULL; uc_compiler_emit_insn(compiler, 0, @@ -659,7 +655,7 @@ uc_compiler_leave_scope(uc_compiler *compiler) } static ssize_t -uc_compiler_declare_local(uc_compiler *compiler, json_object *name) +uc_compiler_declare_local(uc_compiler *compiler, uc_value_t *name) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); uc_locals *locals = &compiler->locals; @@ -675,15 +671,15 @@ uc_compiler_declare_local(uc_compiler *compiler, json_object *name) return -1; } - str1 = json_object_get_string(name); - len1 = json_object_get_string_len(name); + str1 = ucv_string_get(name); + len1 = ucv_string_length(name); for (i = locals->count; i > 0; i--) { - if (locals->entries[i - 1].depth != -1 && locals->entries[i - 1].depth < compiler->scope_depth) + if (locals->entries[i - 1].depth != -1 && locals->entries[i - 1].depth < (ssize_t)compiler->scope_depth) break; - str2 = json_object_get_string(locals->entries[i - 1].name); - len2 = json_object_get_string_len(locals->entries[i - 1].name); + str2 = ucv_string_get(locals->entries[i - 1].name); + len2 = ucv_string_length(locals->entries[i - 1].name); if (len1 == len2 && !strcmp(str1, str2)) { if (compiler->parser->config && @@ -699,7 +695,7 @@ uc_compiler_declare_local(uc_compiler *compiler, json_object *name) uc_vector_grow(locals); - locals->entries[locals->count].name = uc_value_get(name); + locals->entries[locals->count].name = ucv_get(name); locals->entries[locals->count].depth = -1; locals->entries[locals->count].captured = false; locals->entries[locals->count].from = chunk->count; @@ -719,18 +715,18 @@ uc_compiler_initialize_local(uc_compiler *compiler) } static ssize_t -uc_compiler_resolve_local(uc_compiler *compiler, json_object *name) +uc_compiler_resolve_local(uc_compiler *compiler, uc_value_t *name) { uc_locals *locals = &compiler->locals; const char *str1, *str2; size_t i, len1, len2; - str1 = json_object_get_string(name); - len1 = json_object_get_string_len(name); + str1 = ucv_string_get(name); + len1 = ucv_string_length(name); for (i = locals->count; i > 0; i--) { - str2 = json_object_get_string(locals->entries[i - 1].name); - len2 = json_object_get_string_len(locals->entries[i - 1].name); + str2 = ucv_string_get(locals->entries[i - 1].name); + len2 = ucv_string_length(locals->entries[i - 1].name); if (len1 != len2 || strcmp(str1, str2)) continue; @@ -749,9 +745,9 @@ uc_compiler_resolve_local(uc_compiler *compiler, json_object *name) } static ssize_t -uc_compiler_add_upval(uc_compiler *compiler, ssize_t idx, bool local, json_object *name) +uc_compiler_add_upval(uc_compiler *compiler, size_t idx, bool local, uc_value_t *name) { - uc_function *function = compiler->function; + uc_function_t *function = (uc_function_t *)compiler->function; uc_upvals *upvals = &compiler->upvals; uc_upval *uv; size_t i; @@ -771,7 +767,7 @@ uc_compiler_add_upval(uc_compiler *compiler, ssize_t idx, bool local, json_objec upvals->entries[upvals->count].local = local; upvals->entries[upvals->count].index = idx; - upvals->entries[upvals->count].name = uc_value_get(name); + upvals->entries[upvals->count].name = ucv_get(name); function->nupvals++; @@ -779,7 +775,7 @@ uc_compiler_add_upval(uc_compiler *compiler, ssize_t idx, bool local, json_objec } static ssize_t -uc_compiler_resolve_upval(uc_compiler *compiler, json_object *name) +uc_compiler_resolve_upval(uc_compiler *compiler, uc_value_t *name) { ssize_t idx; @@ -989,7 +985,7 @@ uc_compiler_compile_binary(uc_compiler *compiler, bool assignable) } static enum insn_type -uc_compiler_emit_variable_rw(uc_compiler *compiler, json_object *varname, uc_tokentype_t type) +uc_compiler_emit_variable_rw(uc_compiler *compiler, uc_value_t *varname, uc_tokentype_t type) { enum insn_type insn; uint32_t sub_insn; @@ -1050,7 +1046,7 @@ uc_compiler_compile_expression(uc_compiler *compiler) } static bool -uc_compiler_compile_assignment(uc_compiler *compiler, json_object *var) +uc_compiler_compile_assignment(uc_compiler *compiler, uc_value_t *var) { uc_tokentype_t type = compiler->parser->curr.type; @@ -1066,12 +1062,12 @@ uc_compiler_compile_assignment(uc_compiler *compiler, json_object *var) } static bool -uc_compiler_compile_arrowfn(uc_compiler *compiler, json_object *args, bool restarg) +uc_compiler_compile_arrowfn(uc_compiler *compiler, uc_value_t *args, bool restarg) { - bool array = json_object_is_type(args, json_type_array); + bool array = (ucv_type(args) == UC_ARRAY); uc_compiler fncompiler = {}; size_t i, pos, load_off; - uc_function *fn; + uc_function_t *fn; ssize_t slot; if (!uc_compiler_parse_match(compiler, TK_ARROW)) @@ -1080,21 +1076,22 @@ uc_compiler_compile_arrowfn(uc_compiler *compiler, json_object *args, bool resta pos = compiler->parser->prev.pos; uc_compiler_init(&fncompiler, NULL, compiler->parser->prev.pos, - compiler->function->source); + uc_compiler_current_source(compiler)); fncompiler.parent = compiler; fncompiler.parser = compiler->parser; - fncompiler.function->arrow = true; - fncompiler.function->vararg = args ? restarg : false; - fncompiler.function->nargs = array ? json_object_array_length(args) : !!args; + fn = (uc_function_t *)fncompiler.function; + fn->arrow = true; + fn->vararg = args ? restarg : false; + fn->nargs = array ? ucv_array_length(args) : !!args; uc_compiler_enter_scope(&fncompiler); /* declare local variables for arguments */ - for (i = 0; i < fncompiler.function->nargs; i++) { + for (i = 0; i < fn->nargs; i++) { slot = uc_compiler_declare_local(&fncompiler, - array ? json_object_array_get_idx(args, i) : args); + array ? ucv_array_get(args, i) : args); if (slot != -1) uc_compiler_syntax_error(&fncompiler, pos, @@ -1112,8 +1109,8 @@ uc_compiler_compile_arrowfn(uc_compiler *compiler, json_object *args, bool resta uc_compiler_parse_consume(&fncompiler, TK_RBRACE); /* overwrite last pop result with return */ - if (fncompiler.function->chunk.count) { - uc_chunk_pop(&fncompiler.function->chunk); + if (fn->chunk.count) { + uc_chunk_pop(&fn->chunk); uc_compiler_emit_insn(&fncompiler, 0, I_RETURN); } } @@ -1127,7 +1124,7 @@ uc_compiler_compile_arrowfn(uc_compiler *compiler, json_object *args, bool resta load_off = uc_compiler_emit_u32(compiler, 0, 0); /* encode upvalue information */ - for (i = 0; i < fncompiler.function->nupvals; i++) + for (i = 0; i < fn->nupvals; i++) uc_compiler_emit_s32(compiler, 0, fncompiler.upvals.entries[i].local ? -(fncompiler.upvals.entries[i].index + 1) @@ -1139,13 +1136,13 @@ uc_compiler_compile_arrowfn(uc_compiler *compiler, json_object *args, bool resta if (fn) uc_compiler_set_u32(compiler, load_off, uc_chunk_add_constant(uc_compiler_current_chunk(compiler), - fn->header.jso)); + &fn->header)); return true; } static uc_tokentype_t -uc_compiler_compile_var_or_arrowfn(uc_compiler *compiler, bool assignable, json_object *name) +uc_compiler_compile_var_or_arrowfn(uc_compiler *compiler, bool assignable, uc_value_t *name) { uc_tokentype_t rv; @@ -1166,7 +1163,7 @@ uc_compiler_compile_var_or_arrowfn(uc_compiler *compiler, bool assignable, json_ static void uc_compiler_compile_paren(uc_compiler *compiler, bool assignable) { - json_object *varnames = NULL, *varname; + uc_value_t *varnames = NULL, *varname; bool maybe_arrowfn = false; bool restarg = false; @@ -1175,17 +1172,17 @@ uc_compiler_compile_paren(uc_compiler *compiler, bool assignable) while (true) { if (uc_compiler_parse_match(compiler, TK_LABEL)) { if (!varnames) - varnames = xjs_new_array(); + varnames = ucv_array_new(NULL); - json_object_array_add(varnames, uc_value_get(compiler->parser->prev.val)); + ucv_array_push(varnames, ucv_get(compiler->parser->prev.uv)); } else if (uc_compiler_parse_match(compiler, TK_ELLIP)) { uc_compiler_parse_consume(compiler, TK_LABEL); if (!varnames) - varnames = xjs_new_array(); + varnames = ucv_array_new(NULL); - json_object_array_add(varnames, uc_value_get(compiler->parser->prev.val)); + ucv_array_push(varnames, ucv_get(compiler->parser->prev.uv)); uc_compiler_parse_consume(compiler, TK_RPAREN); @@ -1232,8 +1229,8 @@ uc_compiler_compile_paren(uc_compiler *compiler, bool assignable) * consecutive labels. */ if (varnames) { /* Get last variable name */ - varname = json_object_array_get_idx(varnames, - json_object_array_length(varnames) - 1); + varname = ucv_array_get(varnames, + ucv_array_length(varnames) - 1); /* If we consumed the right paren, the expression is complete and we * only need to emit a variable read operation for the last parsed @@ -1279,7 +1276,7 @@ uc_compiler_compile_paren(uc_compiler *compiler, bool assignable) uc_compiler_parse_consume(compiler, TK_RPAREN); out: - uc_value_put(varnames); + ucv_put(varnames); } static void @@ -1358,20 +1355,20 @@ uc_compiler_compile_constant(uc_compiler *compiler, bool assignable) case TK_BOOL: uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, - json_object_get_boolean(compiler->parser->prev.val) ? I_LTRUE : I_LFALSE); + ucv_boolean_get(compiler->parser->prev.uv) ? I_LTRUE : I_LFALSE); break; case TK_DOUBLE: case TK_STRING: - uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.val); + uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.uv); break; case TK_REGEXP: - uc_compiler_emit_regexp(compiler, compiler->parser->prev.pos, compiler->parser->prev.val); + uc_compiler_emit_regexp(compiler, compiler->parser->prev.pos, compiler->parser->prev.uv); break; case TK_NUMBER: - n = json_object_get_int64(compiler->parser->prev.val); + n = ucv_int64_get(compiler->parser->prev.uv); if (n >= -0x7f && n <= 0x7f) { uc_compiler_emit_insn(compiler, compiler->parser->prev.pos, I_LOAD8); @@ -1386,7 +1383,7 @@ uc_compiler_compile_constant(uc_compiler *compiler, bool assignable) uc_compiler_emit_s32(compiler, compiler->parser->prev.pos, n); } else { - uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.val); + uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.uv); } break; @@ -1406,10 +1403,10 @@ uc_compiler_compile_comma(uc_compiler *compiler, bool assignable) static void uc_compiler_compile_labelexpr(uc_compiler *compiler, bool assignable) { - json_object *label = uc_value_get(compiler->parser->prev.val); + uc_value_t *label = ucv_get(compiler->parser->prev.uv); uc_compiler_compile_var_or_arrowfn(compiler, assignable, label); - uc_value_put(label); + ucv_put(label); } static bool @@ -1426,17 +1423,17 @@ static void uc_compiler_compile_function(uc_compiler *compiler, bool assignable) { uc_compiler fncompiler = {}; - json_object *name = NULL; + uc_value_t *name = NULL; ssize_t slot = -1, pos; uc_tokentype_t type; size_t i, load_off; - uc_function *fn; + uc_function_t *fn; pos = compiler->parser->prev.pos; type = compiler->parser->prev.type; if (uc_compiler_parse_match(compiler, TK_LABEL)) { - name = compiler->parser->prev.val; + name = compiler->parser->prev.uv; /* Named functions are syntactic sugar for local variable declaration * with function value assignment. If a name token was encountered, @@ -1448,11 +1445,12 @@ uc_compiler_compile_function(uc_compiler *compiler, bool assignable) } uc_compiler_init(&fncompiler, - name ? json_object_get_string(name) : NULL, compiler->parser->prev.pos, - compiler->function->source); + name ? ucv_string_get(name) : NULL, compiler->parser->prev.pos, + uc_compiler_current_source(compiler)); fncompiler.parent = compiler; fncompiler.parser = compiler->parser; + fn = (uc_function_t *)fncompiler.function; uc_compiler_parse_consume(&fncompiler, TK_LPAREN); @@ -1464,15 +1462,15 @@ uc_compiler_compile_function(uc_compiler *compiler, bool assignable) break; if (uc_compiler_parse_match(&fncompiler, TK_ELLIP)) - fncompiler.function->vararg = true; + fn->vararg = true; if (uc_compiler_parse_match(&fncompiler, TK_LABEL)) { - fncompiler.function->nargs++; + fn->nargs++; - uc_compiler_declare_local(&fncompiler, fncompiler.parser->prev.val); + uc_compiler_declare_local(&fncompiler, fncompiler.parser->prev.uv); uc_compiler_initialize_local(&fncompiler); - if (fncompiler.function->vararg || + if (fn->vararg || !uc_compiler_parse_match(&fncompiler, TK_COMMA)) break; } @@ -1505,7 +1503,7 @@ uc_compiler_compile_function(uc_compiler *compiler, bool assignable) load_off = uc_compiler_emit_u32(compiler, 0, 0); /* encode upvalue information */ - for (i = 0; i < fncompiler.function->nupvals; i++) + for (i = 0; i < fn->nupvals; i++) uc_compiler_emit_s32(compiler, 0, fncompiler.upvals.entries[i].local ? -(fncompiler.upvals.entries[i].index + 1) @@ -1517,7 +1515,7 @@ uc_compiler_compile_function(uc_compiler *compiler, bool assignable) if (fn) uc_compiler_set_u32(compiler, load_off, uc_chunk_add_constant(uc_compiler_current_chunk(compiler), - fn->header.jso)); + &fn->header)); /* if a local variable of the same name already existed, overwrite its value * with the compiled function here */ @@ -1563,7 +1561,7 @@ uc_compiler_compile_dot(uc_compiler *compiler, bool assignable) { /* parse label lhs */ uc_compiler_parse_consume(compiler, TK_LABEL); - uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.val); + uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.uv); /* depending on context, compile into I_UVAL, I_SVAL or I_LVAL operation */ if (!assignable || !uc_compiler_compile_assignment(compiler, NULL)) @@ -1718,7 +1716,7 @@ uc_compiler_compile_object(uc_compiler *compiler, bool assignable) /* load label */ uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, - compiler->parser->prev.val); + compiler->parser->prev.uv); /* If the property name is a plain label followed by a comma or * closing curly brace, treat it as ES2015 property shorthand @@ -1727,7 +1725,7 @@ uc_compiler_compile_object(uc_compiler *compiler, bool assignable) (uc_compiler_parse_check(compiler, TK_COMMA) || uc_compiler_parse_check(compiler, TK_RBRACE))) { uc_compiler_emit_variable_rw(compiler, - compiler->parser->prev.val, 0); + compiler->parser->prev.uv, 0); } /* ... otherwise treat it as ordinary `key: value` tuple */ @@ -1765,7 +1763,7 @@ uc_compiler_compile_object(uc_compiler *compiler, bool assignable) static void -uc_compiler_declare_local_null(uc_compiler *compiler, size_t srcpos, json_object *varname) +uc_compiler_declare_local_null(uc_compiler *compiler, size_t srcpos, uc_value_t *varname) { ssize_t existing_slot = uc_compiler_declare_local(compiler, varname); @@ -1819,7 +1817,7 @@ uc_compiler_declare_internal(uc_compiler *compiler, size_t srcpos, const char *n uc_vector_grow(locals); - locals->entries[locals->count].name = xjs_new_string(name); + locals->entries[locals->count].name = ucv_string_new(name); locals->entries[locals->count].depth = compiler->scope_depth; locals->entries[locals->count].captured = false; locals->entries[locals->count].from = chunk->count; @@ -1838,7 +1836,7 @@ uc_compiler_compile_local(uc_compiler *compiler) uc_compiler_parse_consume(compiler, TK_LABEL); /* declare local variable */ - slot = uc_compiler_declare_local(compiler, compiler->parser->prev.val); + slot = uc_compiler_declare_local(compiler, compiler->parser->prev.uv); /* if followed by '=', parse initializer expression */ if (uc_compiler_parse_match(compiler, TK_ASSIGN)) @@ -2062,10 +2060,10 @@ uc_compiler_compile_for_in(uc_compiler *compiler, bool local, uc_token *kvar, uc /* declare loop variables */ if (local) { - uc_compiler_declare_local_null(compiler, kvar->pos, kvar->val); + uc_compiler_declare_local_null(compiler, kvar->pos, kvar->uv); if (vvar) - uc_compiler_declare_local_null(compiler, vvar->pos, vvar->val); + uc_compiler_declare_local_null(compiler, vvar->pos, vvar->uv); } /* value to iterate */ @@ -2109,12 +2107,12 @@ uc_compiler_compile_for_in(uc_compiler *compiler, bool local, uc_token *kvar, uc /* set key and value variables */ if (vvar) { - uc_compiler_emit_variable_rw(compiler, vvar->val, TK_ASSIGN); + uc_compiler_emit_variable_rw(compiler, vvar->uv, TK_ASSIGN); uc_compiler_emit_insn(compiler, 0, I_POP); } /* set key variable */ - uc_compiler_emit_variable_rw(compiler, kvar->val, TK_ASSIGN); + uc_compiler_emit_variable_rw(compiler, kvar->uv, TK_ASSIGN); uc_compiler_emit_insn(compiler, 0, I_POP); /* compile loop body */ @@ -2171,7 +2169,7 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var) /* We parsed a `local x` or `local x, y` expression, so (re)declare * last label as local initializer variable */ if (local) - uc_compiler_declare_local_null(compiler, var->pos, var->val); + uc_compiler_declare_local_null(compiler, var->pos, var->uv); uc_compiler_compile_labelexpr(compiler, true); uc_compiler_emit_insn(compiler, 0, I_POP); @@ -2269,13 +2267,13 @@ uc_compiler_compile_for(uc_compiler *compiler) if (uc_compiler_parse_match(compiler, TK_LABEL)) { keyvar = compiler->parser->prev; - uc_value_get(keyvar.val); + ucv_get(keyvar.uv); if (uc_compiler_parse_match(compiler, TK_COMMA)) { uc_compiler_parse_consume(compiler, TK_LABEL); valvar = compiler->parser->prev; - uc_value_get(valvar.val); + ucv_get(valvar.uv); } /* is a for-in loop */ @@ -2298,11 +2296,11 @@ uc_compiler_compile_for(uc_compiler *compiler) * as counting for loop... */ uc_compiler_compile_for_count(compiler, local, - valvar.val ? &valvar : (keyvar.val ? &keyvar : NULL)); + valvar.uv ? &valvar : (keyvar.uv ? &keyvar : NULL)); out: - uc_value_put(keyvar.val); - uc_value_put(valvar.val); + ucv_put(keyvar.uv); + ucv_put(valvar.uv); } static void @@ -2523,7 +2521,7 @@ uc_compiler_compile_try(uc_compiler *compiler) if (uc_compiler_parse_match(compiler, TK_LPAREN)) { uc_compiler_parse_consume(compiler, TK_LABEL); - uc_compiler_declare_local(compiler, compiler->parser->prev.val); + uc_compiler_declare_local(compiler, compiler->parser->prev.uv); uc_compiler_initialize_local(compiler); uc_compiler_parse_consume(compiler, TK_RPAREN); @@ -2565,7 +2563,7 @@ uc_compiler_compile_control(uc_compiler *compiler) } /* pop locals in scope up to this point */ - for (i = locals->count; i > 0 && locals->entries[i - 1].depth == compiler->scope_depth; i--) + for (i = locals->count; i > 0 && (size_t)locals->entries[i - 1].depth == compiler->scope_depth; i--) uc_compiler_emit_insn(compiler, 0, I_POP); uc_vector_grow(p); @@ -2614,7 +2612,7 @@ uc_compiler_compile_tplexp(uc_compiler *compiler) static void uc_compiler_compile_text(uc_compiler *compiler) { - uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.val); + uc_compiler_emit_constant(compiler, compiler->parser->prev.pos, compiler->parser->prev.uv); uc_compiler_emit_insn(compiler, 0, I_PRINT); } @@ -2705,12 +2703,12 @@ uc_compiler_compile_declaration(uc_compiler *compiler) uc_compiler_parse_synchronize(compiler); } -uc_function * +uc_function_t * uc_compile(uc_parse_config *config, uc_source *source, char **errp) { uc_parser parser = { .config = config }; uc_compiler compiler = { .parser = &parser }; - uc_function *fn; + uc_function_t *fn; uc_lexer_init(&parser.lex, config, source); uc_compiler_init(&compiler, "main", 0, source); @@ -2722,8 +2720,13 @@ uc_compile(uc_parse_config *config, uc_source *source, char **errp) fn = uc_compiler_finish(&compiler); - if (errp) - *errp = parser.error; + if (errp) { + *errp = parser.error ? parser.error->buf : NULL; + free(parser.error); + } + else { + printbuf_free(parser.error); + } uc_lexer_free(&parser.lex); diff --git a/compiler.h b/compiler.h index 53ff987..7b241ff 100644 --- a/compiler.h +++ b/compiler.h @@ -28,8 +28,8 @@ #endif #include "source.h" -#include "object.h" #include "lexer.h" +#include "types.h" #include "util.h" typedef enum { @@ -72,14 +72,14 @@ struct uc_patchlist { typedef struct uc_patchlist uc_patchlist; typedef struct { - json_object *name; + uc_value_t *name; ssize_t depth; size_t from; bool captured; } uc_local; typedef struct { - json_object *name; + uc_value_t *name; size_t index; bool local; } uc_upval; @@ -93,7 +93,7 @@ typedef struct { uc_lexer lex; uc_token prev, curr; bool synchronizing; - char *error; + uc_stringbuf_t *error; } uc_parser; struct uc_compiler { @@ -101,7 +101,7 @@ struct uc_compiler { uc_locals locals; uc_upvals upvals; uc_patchlist *patchlist; - uc_function *function; + uc_value_t *function; uc_parser *parser; size_t scope_depth, current_srcpos, last_insn; bool statement_emitted; @@ -115,6 +115,6 @@ typedef struct { uc_precedence_t precedence; } uc_parse_rule; -uc_function *uc_compile(uc_parse_config *config, uc_source *source, char **errp); +uc_function_t *uc_compile(uc_parse_config *config, uc_source *source, char **errp); #endif /* __COMPILER_H_ */ diff --git a/lexer.c b/lexer.c index 1fba88f..74e0c4d 100644 --- a/lexer.c +++ b/lexer.c @@ -32,9 +32,9 @@ #define UC_LEX_CONTINUE_PARSING (void *)1 struct keyword { - int type; + unsigned type; const char *pat; - int plen; + unsigned plen; union { double d; bool b; @@ -42,12 +42,12 @@ struct keyword { }; struct token { - int type; + unsigned type; union { uint32_t patn; char pat[4]; }; - int plen; + unsigned plen; uc_token *(*parse)(uc_lexer *, bool); }; @@ -223,10 +223,10 @@ utf8enc(char **out, int *rem, int code) #define UT_LEX_MAX_TOKEN_LEN 3 static uc_token * -emit_op(uc_lexer *lex, uint32_t pos, int type, struct json_object *val) +emit_op(uc_lexer *lex, uint32_t pos, int type, uc_value_t *uv) { lex->curr.type = type; - lex->curr.val = val; + lex->curr.uv = uv; lex->curr.pos = pos; return &lex->curr; @@ -257,7 +257,7 @@ lookbehind_to_text(uc_lexer *lex, uint32_t pos, int type, const char *strip_trai lex->lookbehindlen--; } - rv = emit_op(lex, pos, type, xjs_new_string_len(lex->lookbehind, lex->lookbehindlen)); + rv = emit_op(lex, pos, type, ucv_string_new_length(lex->lookbehind, lex->lookbehindlen)); lookbehind_reset(lex); } @@ -360,7 +360,7 @@ parse_comment(uc_lexer *lex, bool no_regexp) size_t elen; if (!buf_remaining(lex)) - return emit_op(lex, lex->lastoff, TK_ERROR, xjs_new_string("Unterminated comment")); + return emit_op(lex, lex->lastoff, TK_ERROR, ucv_string_new("Unterminated comment")); if (!strcmp(tok->pat, "//")) { end = "\n"; @@ -406,7 +406,7 @@ parse_string(uc_lexer *lex, bool no_regexp) int code; if (!buf_remaining(lex)) - return emit_op(lex, lex->lastoff, TK_ERROR, xjs_new_string("Unterminated string")); + return emit_op(lex, lex->lastoff, TK_ERROR, ucv_string_new("Unterminated string")); for (ptr = lex->bufstart; ptr < lex->bufend; ptr++) { /* continuation of escape sequence */ @@ -461,7 +461,7 @@ parse_string(uc_lexer *lex, bool no_regexp) case 'u': if (lex->esclen < 5) { if (!isxdigit(*ptr)) - return emit_op(lex, lex->source->off + lex->esclen + 1, TK_ERROR, xjs_new_string("Invalid escape sequence")); + return emit_op(lex, lex->source->off + lex->esclen + 1, TK_ERROR, ucv_string_new("Invalid escape sequence")); lex->esc[lex->esclen++] = *ptr; } @@ -513,7 +513,7 @@ parse_string(uc_lexer *lex, bool no_regexp) case 'x': if (lex->esclen < 3) { if (!isxdigit(*ptr)) - return emit_op(lex, lex->source->off + lex->esclen + 1, TK_ERROR, xjs_new_string("Invalid escape sequence")); + return emit_op(lex, lex->source->off + lex->esclen + 1, TK_ERROR, ucv_string_new("Invalid escape sequence")); lex->esc[lex->esclen++] = *ptr; } @@ -564,7 +564,7 @@ parse_string(uc_lexer *lex, bool no_regexp) dec(lex->esc[3]); if (code > 255) - return emit_op(lex, lex->source->off + lex->esclen + 1, TK_ERROR, xjs_new_string("Invalid escape sequence")); + return emit_op(lex, lex->source->off + lex->esclen + 1, TK_ERROR, ucv_string_new("Invalid escape sequence")); append_utf8(lex, code); @@ -585,7 +585,7 @@ parse_string(uc_lexer *lex, bool no_regexp) rv = lookbehind_to_text(lex, lex->lastoff, TK_STRING, NULL); if (!rv) - rv = emit_op(lex, lex->lastoff, TK_STRING, xjs_new_string_len("", 0)); + rv = emit_op(lex, lex->lastoff, TK_STRING, ucv_string_new_length("", 0)); return rv; } @@ -685,10 +685,11 @@ parse_regexp(uc_lexer *lex, bool no_regexp) len = xasprintf(&s, "%c%*s", (is_reg_global << 0) | (is_reg_icase << 1) | (is_reg_newline << 2), - json_object_get_string_len(rv->val), - json_object_get_string(rv->val)); + ucv_string_length(rv->uv), + ucv_string_get(rv->uv)); - json_object_set_string_len(rv->val, s, len); + ucv_free(rv->uv, false); + rv->uv = ucv_string_new_length(s, len); free(s); rv->type = TK_REGEXP; @@ -733,11 +734,11 @@ parse_label(uc_lexer *lex, bool no_regexp) switch (word->type) { case TK_DOUBLE: - rv = emit_op(lex, lex->source->off - word->plen, word->type, uc_double_new(word->d)); + rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_double_new(word->d)); break; case TK_BOOL: - rv = emit_op(lex, lex->source->off - word->plen, word->type, xjs_new_boolean(word->b)); + rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_boolean_new(word->b)); break; default: @@ -806,19 +807,19 @@ parse_number(uc_lexer *lex, bool no_regexp) d = -d; if (e > lex->lookbehind && *e == 0) - rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_DOUBLE, uc_double_new(d)); + rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_DOUBLE, ucv_double_new(d)); else - rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, xjs_new_string("Invalid number literal")); + rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, ucv_string_new("Invalid number literal")); } else if (*e == 0) { if (tok->pat[0] == '-') n = (errno == ERANGE) ? INT64_MIN : -n; - rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_NUMBER, xjs_new_int64(n)); + rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_NUMBER, ucv_int64_new(n)); //OP(rv)->is_overflow = (errno == ERANGE); } else { - rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, xjs_new_string("Invalid number literal")); + rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, ucv_string_new("Invalid number literal")); } lookbehind_reset(lex); @@ -1008,7 +1009,7 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) buf_consume(lex, lex->bufend - lex->bufstart); - return emit_op(lex, lex->lastoff, TK_ERROR, xjs_new_string("Unterminated template block")); + return emit_op(lex, lex->lastoff, TK_ERROR, ucv_string_new("Unterminated template block")); } break; @@ -1059,7 +1060,7 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) if (tok->type == TK_LSTM || tok->type == TK_LEXP) { buf_consume(lex, tok->plen); - return emit_op(lex, lex->source->off - tok->plen, TK_ERROR, xjs_new_string("Template blocks may not be nested")); + return emit_op(lex, lex->source->off - tok->plen, TK_ERROR, ucv_string_new("Template blocks may not be nested")); } /* found end of block */ @@ -1093,7 +1094,7 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) /* no token matched and we do have remaining data, junk */ if (buf_remaining(lex)) - return emit_op(lex, lex->source->off, TK_ERROR, xjs_new_string("Unexpected character")); + return emit_op(lex, lex->source->off, TK_ERROR, ucv_string_new("Unexpected character")); /* we're at eof, allow unclosed statement blocks */ if (lex->block == STATEMENTS) { @@ -1103,7 +1104,7 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) } /* premature EOF */ - return emit_op(lex, lex->source->off, TK_ERROR, xjs_new_string("Unterminated template block")); + return emit_op(lex, lex->source->off, TK_ERROR, ucv_string_new("Unterminated template block")); case UT_LEX_PARSE_TOKEN: @@ -1188,7 +1189,7 @@ uc_lexer_next_token(uc_lexer *lex, bool no_regexp) } const char * -uc_get_tokenname(int type) +uc_get_tokenname(unsigned type) { static char buf[sizeof("'endfunction'")]; size_t i; diff --git a/lexer.h b/lexer.h index 820a986..f023fb6 100644 --- a/lexer.h +++ b/lexer.h @@ -18,6 +18,7 @@ #define __LEXER_H_ #include "source.h" +#include "types.h" typedef enum { @@ -121,7 +122,7 @@ typedef enum { typedef struct { uc_tokentype_t type; - json_object *val; + uc_value_t *uv; size_t pos; } uc_token; @@ -170,6 +171,6 @@ uc_token *uc_lexer_next_token(uc_lexer *lex, bool no_regexp); bool utf8enc(char **out, int *rem, int code); const char * -uc_get_tokenname(int type); +uc_get_tokenname(unsigned type); #endif /* __LEXER_H_ */ diff --git a/lib.c b/lib.c index ea87a6b..f4e69c7 100644 --- a/lib.c +++ b/lib.c @@ -35,116 +35,59 @@ #include "compiler.h" #include "vm.h" #include "lib.h" -#include "object.h" - - -const uc_ops uc = { - .value = { - .proto = uc_prototype_new, - .cfunc = uc_cfunction_new, - .dbl = uc_double_new, - .regexp = uc_regexp_new, - .tonumber = uc_cast_number, - .ressource = uc_ressource_new - }, - - .ressource = { - .define = uc_ressource_type_add, - .create = uc_ressource_new, - .data = uc_ressource_dataptr, - .proto = uc_ressource_prototype - }, - - .vm = { - .call = uc_vm_call, - .peek = uc_vm_stack_peek, - .pop = uc_vm_stack_pop, - .push = uc_vm_stack_push, - .raise = uc_vm_raise_exception - } -}; - -const uc_ops *ops = &uc; - -__attribute__((format(printf, 3, 5))) static void -snprintf_append(char **dptr, size_t *dlen, const char *fmt, ssize_t sz, ...) -{ - va_list ap; - char *tmp; - int n; - - va_start(ap, sz); - n = vsnprintf(NULL, 0, fmt, ap); - va_end(ap); - - if (sz >= 0 && n > sz) - n = sz; - - tmp = xrealloc(*dptr, *dlen + n + 1); - - va_start(ap, sz); - vsnprintf(tmp + *dlen, n + 1, fmt, ap); - va_end(ap); - - *dptr = tmp; - *dlen += n; -} - -#define sprintf_append(dptr, dlen, fmt, ...) \ - snprintf_append(dptr, dlen, fmt, -1, ##__VA_ARGS__) static void -format_context_line(char **msg, size_t *msglen, const char *line, size_t off, bool compact) +format_context_line(uc_stringbuf_t *buf, const char *line, size_t off, bool compact) { + unsigned padlen, i; const char *p; - int padlen, i; for (p = line, padlen = 0; *p != '\n' && *p != '\0'; p++) { - if (compact && (p - line) == off) - sprintf_append(msg, msglen, "\033[22m"); + if (compact && (p - line) == (ptrdiff_t)off) + ucv_stringbuf_append(buf, "\033[22m"); switch (*p) { case '\t': - sprintf_append(msg, msglen, " "); + ucv_stringbuf_append(buf, " "); if (p < line + off) padlen += 4; break; case '\r': case '\v': - sprintf_append(msg, msglen, " "); + ucv_stringbuf_append(buf, " "); if (p < line + off) padlen++; break; default: - sprintf_append(msg, msglen, "%c", *p); + ucv_stringbuf_addstr(buf, p, 1); if (p < line + off) padlen++; } } if (compact) { - sprintf_append(msg, msglen, "\033[m\n"); + ucv_stringbuf_append(buf, "\033[m\n"); return; } - sprintf_append(msg, msglen, "`\n "); + ucv_stringbuf_append(buf, "`\n "); if (padlen < strlen("Near here ^")) { for (i = 0; i < padlen; i++) - sprintf_append(msg, msglen, " "); + ucv_stringbuf_append(buf, " "); - sprintf_append(msg, msglen, "^-- Near here\n"); + ucv_stringbuf_append(buf, "^-- Near here\n"); } else { - sprintf_append(msg, msglen, "Near here "); + ucv_stringbuf_append(buf, "Near here "); for (i = strlen("Near here "); i < padlen; i++) - sprintf_append(msg, msglen, "-"); + ucv_stringbuf_append(buf, "-"); - sprintf_append(msg, msglen, "^\n"); + ucv_stringbuf_append(buf, "^\n"); } } @@ -163,19 +106,19 @@ source_filename(uc_source *src, uint32_t line) return buf; } -void -format_source_context(char **msg, size_t *msglen, uc_source *src, size_t off, bool compact) +bool +format_source_context(uc_stringbuf_t *buf, uc_source *src, size_t off, bool compact) { size_t len, rlen; bool truncated; - char buf[256]; + char line[256]; long srcpos; int eline; srcpos = ftell(src->fp); if (srcpos == -1) - return; + return false; fseek(src->fp, 0, SEEK_SET); @@ -183,93 +126,105 @@ format_source_context(char **msg, size_t *msglen, uc_source *src, size_t off, bo eline = 1; rlen = 0; - while (fgets(buf, sizeof(buf), src->fp)) { - len = strlen(buf); + while (fgets(line, sizeof(line), src->fp)) { + len = strlen(line); rlen += len; if (rlen > off) { if (compact) - sprintf_append(msg, msglen, "\033[2;40;97m%17s %s", + ucv_stringbuf_printf(buf, "\033[2;40;97m%17s %s", source_filename(src, eline), truncated ? "..." : ""); else - sprintf_append(msg, msglen, "\n `%s", + ucv_stringbuf_printf(buf, "\n `%s", truncated ? "..." : ""); - format_context_line(msg, msglen, buf, len - (rlen - off) + (truncated ? 3 : 0), compact); + format_context_line(buf, line, len - (rlen - off) + (truncated ? 3 : 0), compact); break; } - truncated = (len > 0 && buf[len-1] != '\n'); + truncated = (len > 0 && line[len-1] != '\n'); eline += !truncated; } fseek(src->fp, srcpos, SEEK_SET); + + return true; } -void -format_error_context(char **msg, size_t *msglen, uc_source *src, json_object *stacktrace, size_t off) +bool +format_error_context(uc_stringbuf_t *buf, uc_source *src, uc_value_t *stacktrace, size_t off) { - json_object *e, *fn, *file, *line, *byte; + uc_value_t *e, *fn, *file, *line, *byte; const char *path; size_t idx; - for (idx = 0; idx < (stacktrace ? json_object_array_length(stacktrace) : 0); idx++) { - e = json_object_array_get_idx(stacktrace, idx); - fn = json_object_object_get(e, "function"); - file = json_object_object_get(e, "filename"); + for (idx = 0; idx < (stacktrace ? ucv_array_length(stacktrace) : 0); idx++) { + e = ucv_array_get(stacktrace, idx); + fn = ucv_object_get(e, "function", NULL); + file = ucv_object_get(e, "filename", NULL); if (idx == 0) { - path = (file && strcmp(json_object_get_string(file), "[stdin]")) - ? json_object_get_string(file) : NULL; + path = (file && strcmp(ucv_string_get(file), "[stdin]")) + ? ucv_string_get(file) : NULL; if (path && fn) - sprintf_append(msg, msglen, "In %s(), file %s, ", - json_object_get_string(fn), path); + ucv_stringbuf_printf(buf, "In %s(), file %s, ", ucv_string_get(fn), path); else if (fn) - sprintf_append(msg, msglen, "In %s(), ", - json_object_get_string(fn)); + ucv_stringbuf_printf(buf, "In %s(), ", ucv_string_get(fn)); else if (path) - sprintf_append(msg, msglen, "In %s, ", path); + ucv_stringbuf_printf(buf, "In %s, ", path); else - sprintf_append(msg, msglen, "In "); + ucv_stringbuf_append(buf, "In "); - sprintf_append(msg, msglen, "line %" PRId64 ", byte %" PRId64 ":\n", - json_object_get_int64(json_object_object_get(e, "line")), - json_object_get_int64(json_object_object_get(e, "byte"))); + ucv_stringbuf_printf(buf, "line %" PRId64 ", byte %" PRId64 ":\n", + ucv_int64_get(ucv_object_get(e, "line", NULL)), + ucv_int64_get(ucv_object_get(e, "byte", NULL))); } else { - line = json_object_object_get(e, "line"); - byte = json_object_object_get(e, "byte"); + line = ucv_object_get(e, "line", NULL); + byte = ucv_object_get(e, "byte", NULL); - sprintf_append(msg, msglen, " called from %s%s (%s", - fn ? "function " : "anonymous function", - fn ? json_object_get_string(fn) : "", - file ? json_object_get_string(file) : ""); + ucv_stringbuf_printf(buf, " called from %s%s (%s", + fn ? "function " : "anonymous function", + fn ? ucv_string_get(fn) : "", + file ? ucv_string_get(file) : ""); if (line && byte) - sprintf_append(msg, msglen, ":%" PRId64 ":%" PRId64 ")\n", - json_object_get_int64(line), - json_object_get_int64(byte)); + ucv_stringbuf_printf(buf, ":%" PRId64 ":%" PRId64 ")\n", + ucv_int64_get(line), + ucv_int64_get(byte)); else - sprintf_append(msg, msglen, "[C])\n"); + ucv_stringbuf_append(buf, "[C])\n"); } } - format_source_context(msg, msglen, src, off, false); + return format_source_context(buf, src, off, false); +} + +static char *uc_cast_string(uc_vm *vm, uc_value_t **v, bool *freeable) { + if (ucv_type(*v) == UC_STRING) { + *freeable = false; + + return _ucv_string_get(v); + } + + *freeable = true; + + return ucv_to_string(vm, *v); } static double -uc_cast_double(json_object *v) +uc_cast_double(uc_value_t *v) { - enum json_type t; + uc_type_t t; int64_t n; double d; t = uc_cast_number(v, &n, &d); errno = 0; - if (t == json_type_double) { + if (t == UC_DOUBLE) { if (isnan(d)) errno = EINVAL; else if (!isfinite(d)) @@ -282,16 +237,16 @@ uc_cast_double(json_object *v) } static int64_t -uc_cast_int64(json_object *v) +uc_cast_int64(uc_value_t *v) { - enum json_type t; + uc_type_t t; int64_t n; double d; t = uc_cast_number(v, &n, &d); errno = 0; - if (t == json_type_double) { + if (t == UC_DOUBLE) { if (isnan(d)) errno = EINVAL; else if (!isfinite(d)) @@ -305,73 +260,72 @@ uc_cast_int64(json_object *v) return n; } -static json_object * +static uc_value_t * uc_print_common(uc_vm *vm, size_t nargs, FILE *fh) { - json_object *item; + uc_value_t *item; size_t reslen = 0; size_t len = 0; size_t arridx; - const char *p; + char *p; for (arridx = 0; arridx < nargs; arridx++) { item = uc_get_arg(arridx); - if (json_object_is_type(item, json_type_string)) { - p = json_object_get_string(item); - len = json_object_get_string_len(item); + if (ucv_type(item) == UC_STRING) { + len = ucv_string_length(item); + reslen += fwrite(ucv_string_get(item), 1, len, fh); } - else { - p = item ? json_object_get_string(item) : NULL; - p = p ? p : ""; + else if (item != NULL) { + p = ucv_to_string(vm, item); len = strlen(p); + reslen += fwrite(p, 1, len, fh); + free(p); } - - reslen += fwrite(p, 1, len, fh); } - return xjs_new_int64(reslen); + return ucv_int64_new(reslen); } -static json_object * +static uc_value_t * uc_print(uc_vm *vm, size_t nargs) { return uc_print_common(vm, nargs, stdout); } -static json_object * +static uc_value_t * uc_length(uc_vm *vm, size_t nargs) { - json_object *arg = uc_get_arg(0); + uc_value_t *arg = uc_get_arg(0); - switch (json_object_get_type(arg)) { - case json_type_object: - return xjs_new_int64(json_object_object_length(arg)); + switch (ucv_type(arg)) { + case UC_OBJECT: + return ucv_int64_new(ucv_object_length(arg)); - case json_type_array: - return xjs_new_int64(json_object_array_length(arg)); + case UC_ARRAY: + return ucv_int64_new(ucv_array_length(arg)); - case json_type_string: - return xjs_new_int64(json_object_get_string_len(arg)); + case UC_STRING: + return ucv_int64_new(ucv_string_length(arg)); default: return NULL; } } -static json_object * +static uc_value_t * uc_index(uc_vm *vm, size_t nargs, bool right) { - json_object *stack = uc_get_arg(0); - json_object *needle = uc_get_arg(1); + uc_value_t *stack = uc_get_arg(0); + uc_value_t *needle = uc_get_arg(1); size_t arridx, len, ret = -1; const char *sstr, *nstr, *p; - switch (json_object_get_type(stack)) { - case json_type_array: - for (arridx = 0, len = json_object_array_length(stack); arridx < len; arridx++) { - if (uc_cmp(TK_EQ, json_object_array_get_idx(stack, arridx), needle)) { + switch (ucv_type(stack)) { + case UC_ARRAY: + for (arridx = 0, len = ucv_array_length(stack); arridx < len; arridx++) { + if (uc_cmp(TK_EQ, ucv_array_get(stack, arridx), needle)) { ret = arridx; if (!right) @@ -379,11 +333,11 @@ uc_index(uc_vm *vm, size_t nargs, bool right) } } - return xjs_new_int64(ret); + return ucv_int64_new(ret); - case json_type_string: - sstr = json_object_get_string(stack); - nstr = needle ? json_object_get_string(needle) : NULL; + case UC_STRING: + sstr = ucv_string_get(stack); + nstr = needle ? ucv_string_get(needle) : NULL; len = needle ? strlen(nstr) : 0; for (p = sstr; *p && len; p++) { @@ -395,126 +349,87 @@ uc_index(uc_vm *vm, size_t nargs, bool right) } } - return xjs_new_int64(ret); + return ucv_int64_new(ret); default: return NULL; } } -static json_object * +static uc_value_t * uc_lindex(uc_vm *vm, size_t nargs) { return uc_index(vm, nargs, false); } -static json_object * +static uc_value_t * uc_rindex(uc_vm *vm, size_t nargs) { return uc_index(vm, nargs, true); } -static json_object * +static uc_value_t * uc_push(uc_vm *vm, size_t nargs) { - json_object *arr = uc_get_arg(0); - json_object *item = NULL; + uc_value_t *arr = uc_get_arg(0); + uc_value_t *item = NULL; size_t arridx; - if (!json_object_is_type(arr, json_type_array)) + if (ucv_type(arr) != UC_ARRAY) return NULL; for (arridx = 1; arridx < nargs; arridx++) { item = uc_get_arg(arridx); - json_object_array_add(arr, uc_value_get(item)); + ucv_array_push(arr, ucv_get(item)); } - return uc_value_get(item); + return ucv_get(item); } -static json_object * +static uc_value_t * uc_pop(uc_vm *vm, size_t nargs) { - json_object *arr = uc_get_arg(0); - json_object *item = NULL; - size_t arrlen; + uc_value_t *arr = uc_get_arg(0); - if (!json_object_is_type(arr, json_type_array)) - return NULL; - - arrlen = json_object_array_length(arr); - - if (arrlen > 0) { - item = uc_value_get(json_object_array_get_idx(arr, arrlen - 1)); - json_object_array_del_idx(arr, arrlen - 1, 1); -#ifdef HAVE_ARRAY_SHRINK - json_object_array_shrink(arr, 0); -#endif - } - - return item; + return ucv_array_pop(arr); } -static json_object * +static uc_value_t * uc_shift(uc_vm *vm, size_t nargs) { - json_object *arr = uc_get_arg(0); - json_object *item = NULL; - size_t arridx, arrlen; - - if (!json_object_is_type(arr, json_type_array)) - return NULL; - - item = uc_value_get(json_object_array_get_idx(arr, 0)); - arrlen = json_object_array_length(arr); - - for (arridx = 0; arridx < arrlen - 1; arridx++) - json_object_array_put_idx(arr, arridx, - uc_value_get(json_object_array_get_idx(arr, arridx + 1))); + uc_value_t *arr = uc_get_arg(0); - json_object_array_del_idx(arr, arrlen - 1, 1); -#ifdef HAVE_ARRAY_SHRINK - json_object_array_shrink(arr, 0); -#endif - - return item; + return ucv_array_shift(arr); } -static json_object * +static uc_value_t * uc_unshift(uc_vm *vm, size_t nargs) { - json_object *arr = uc_get_arg(0); - json_object *item = NULL; - size_t arridx, arrlen, addlen; + uc_value_t *arr = uc_get_arg(0); + uc_value_t *item = NULL; + size_t i; - if (!json_object_is_type(arr, json_type_array)) + if (ucv_type(arr) != UC_ARRAY) return NULL; - arrlen = json_object_array_length(arr); - addlen = nargs - 1; - - for (arridx = arrlen; arridx > 0; arridx--) - json_object_array_put_idx(arr, arridx + addlen - 1, - uc_value_get(json_object_array_get_idx(arr, arridx - 1))); - - for (arridx = 0; arridx < addlen; arridx++) { - item = uc_get_arg(arridx + 1); - json_object_array_put_idx(arr, arridx, uc_value_get(item)); + for (i = 1; i < nargs; i++) { + item = uc_get_arg(i); + ucv_array_unshift(arr, ucv_get(item)); } - return uc_value_get(item); + return ucv_get(item); } -static json_object * +static uc_value_t * uc_chr(uc_vm *vm, size_t nargs) { - json_object *rv = NULL; + uc_value_t *rv = NULL; size_t idx; int64_t n; char *str; if (!nargs) - return xjs_new_string_len("", 0); + return ucv_string_new_length("", 0); str = xalloc(nargs); @@ -529,58 +444,68 @@ uc_chr(uc_vm *vm, size_t nargs) str[idx] = (char)n; } - rv = xjs_new_string_len(str, nargs); + rv = ucv_string_new_length(str, nargs); free(str); return rv; } -static json_object * +static uc_value_t * uc_delete(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - json_object *rv = NULL; + uc_value_t *obj = uc_get_arg(0); + uc_value_t *rv = NULL; const char *key; - size_t arridx; + size_t i; - if (!json_object_is_type(obj, json_type_object)) + if (ucv_type(obj) != UC_OBJECT) return NULL; - for (arridx = 1; arridx < nargs; arridx++) { - uc_value_put(rv); + for (i = 1; i < nargs; i++) { + ucv_put(rv); - key = json_object_get_string(uc_get_arg(arridx)); - rv = uc_value_get(json_object_object_get(obj, key ? key : "null")); + key = ucv_string_get(uc_get_arg(i)); + rv = ucv_get(ucv_object_get(obj, key ? key : "null", NULL)); - json_object_object_del(obj, key ? key : "null"); + ucv_object_delete(obj, key ? key : "null"); } return rv; } -static json_object * +static uc_value_t * uc_die(uc_vm *vm, size_t nargs) { - const char *msg = json_object_get_string(uc_get_arg(0)); + uc_value_t *msg = uc_get_arg(0); + bool freeable = false; + char *s; + + s = msg ? uc_cast_string(vm, &msg, &freeable) : "Died"; - uc_vm_raise_exception(vm, EXCEPTION_USER, msg ? msg : "Died"); + uc_vm_raise_exception(vm, EXCEPTION_USER, s); + + if (freeable) + free(s); return NULL; } -static json_object * +static uc_value_t * uc_exists(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - const char *key = json_object_get_string(uc_get_arg(1)); + uc_value_t *obj = uc_get_arg(0); + const char *key = ucv_string_get(uc_get_arg(1)); + bool found; - if (!json_object_is_type(obj, json_type_object)) + if (ucv_type(obj) != UC_OBJECT) return false; - return xjs_new_boolean(json_object_object_get_ex(obj, key ? key : "null", NULL)); + ucv_object_get(obj, key ? key : "null", &found); + + return ucv_boolean_new(found); } -__attribute__((noreturn)) static json_object * +__attribute__((noreturn)) static uc_value_t * uc_exit(uc_vm *vm, size_t nargs) { int64_t n = uc_cast_int64(uc_get_arg(0)); @@ -588,37 +513,37 @@ uc_exit(uc_vm *vm, size_t nargs) exit(n); } -static json_object * +static uc_value_t * uc_getenv(uc_vm *vm, size_t nargs) { - const char *key = json_object_get_string(uc_get_arg(0)); + const char *key = ucv_string_get(uc_get_arg(0)); char *val = key ? getenv(key) : NULL; - return val ? xjs_new_string(val) : NULL; + return val ? ucv_string_new(val) : NULL; } -static json_object * +static uc_value_t * uc_filter(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - json_object *func = uc_get_arg(1); - json_object *rv, *arr; + uc_value_t *obj = uc_get_arg(0); + uc_value_t *func = uc_get_arg(1); + uc_value_t *rv, *arr; size_t arridx, arrlen; - if (!json_object_is_type(obj, json_type_array)) + if (ucv_type(obj) != UC_ARRAY) return NULL; - arr = xjs_new_array(); + arr = ucv_array_new(vm); - for (arrlen = json_object_array_length(obj), arridx = 0; arridx < arrlen; arridx++) { + for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) { /* XXX: revisit leaks */ - uc_vm_stack_push(vm, uc_value_get(func)); - uc_vm_stack_push(vm, uc_value_get(json_object_array_get_idx(obj, arridx))); - uc_vm_stack_push(vm, xjs_new_int64(arridx)); - uc_vm_stack_push(vm, uc_value_get(obj)); + uc_vm_stack_push(vm, ucv_get(func)); + uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx))); + uc_vm_stack_push(vm, ucv_int64_new(arridx)); + uc_vm_stack_push(vm, ucv_get(obj)); if (uc_vm_call(vm, false, 3)) { - uc_value_put(arr); + ucv_put(arr); return NULL; } @@ -626,281 +551,229 @@ uc_filter(uc_vm *vm, size_t nargs) rv = uc_vm_stack_pop(vm); if (uc_val_is_truish(rv)) - json_object_array_add(arr, uc_value_get(json_object_array_get_idx(obj, arridx))); + ucv_array_push(arr, ucv_get(ucv_array_get(obj, arridx))); - uc_value_put(rv); + ucv_put(rv); } return arr; } -static json_object * +static uc_value_t * uc_hex(uc_vm *vm, size_t nargs) { - const char *val = json_object_get_string(uc_get_arg(0)); + const char *val = ucv_string_get(uc_get_arg(0)); int64_t n; char *e; if (!val || !isxdigit(*val)) - return uc_double_new(NAN); + return ucv_double_new(NAN); n = strtoll(val, &e, 16); if (e == val || *e) - return uc_double_new(NAN); + return ucv_double_new(NAN); - return xjs_new_int64(n); + return ucv_int64_new(n); } -static json_object * +static uc_value_t * uc_int(uc_vm *vm, size_t nargs) { int64_t n = uc_cast_int64(uc_get_arg(0)); if (errno == EINVAL || errno == EOVERFLOW) - return uc_double_new(NAN); + return ucv_double_new(NAN); - return xjs_new_int64(n); + return ucv_int64_new(n); } -static json_object * +static uc_value_t * uc_join(uc_vm *vm, size_t nargs) { - const char *sep = json_object_get_string(uc_get_arg(0)); - json_object *arr = uc_get_arg(1); - json_object *rv = NULL; - size_t arrlen, arridx, len = 1; - const char *item; - char *res, *p; - int ret; + uc_value_t *sep = uc_get_arg(0); + uc_value_t *arr = uc_get_arg(1); + size_t arrlen, arridx; + uc_stringbuf_t *buf; - if (!json_object_is_type(arr, json_type_array)) + if (ucv_type(arr) != UC_ARRAY) return NULL; - for (arrlen = json_object_array_length(arr), arridx = 0; arridx < arrlen; arridx++) { - if (arridx > 0) - len += strlen(sep); - - item = json_object_get_string(json_object_array_get_idx(arr, arridx)); - len += item ? strlen(item) : 0; - } - - p = res = xalloc(len); - - for (arrlen = json_object_array_length(arr), arridx = 0; arridx < arrlen; arridx++) { - if (arridx > 0) { - ret = snprintf(p, len, "%s", sep); - - if (ret < 0 || ret >= len) - goto out; + buf = ucv_stringbuf_new(); - len -= ret; - p += ret; - } - - item = json_object_get_string(json_object_array_get_idx(arr, arridx)); - - if (item) { - ret = snprintf(p, len, "%s", item); - - if (ret < 0 || ret >= len) - goto out; + for (arrlen = ucv_array_length(arr), arridx = 0; arridx < arrlen; arridx++) { + if (arridx > 0) + ucv_to_stringbuf(vm, buf, sep, false); - len -= ret; - p += ret; - } + ucv_to_stringbuf(vm, buf, ucv_array_get(arr, arridx), false); } - rv = xjs_new_string(res); - -out: - free(res); - - return rv; + return ucv_stringbuf_finish(buf); } -static json_object * +static uc_value_t * uc_keys(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - json_object *arr = NULL; + uc_value_t *obj = uc_get_arg(0); + uc_value_t *arr = NULL; - if (!json_object_is_type(obj, json_type_object)) + if (ucv_type(obj) != UC_OBJECT) return NULL; - arr = xjs_new_array(); + arr = ucv_array_new(vm); - json_object_object_foreach(obj, key, val) - json_object_array_add(arr, xjs_new_string(key)); + ucv_object_foreach(obj, key, val) + ucv_array_push(arr, ucv_string_new(key)); return arr; } -static json_object * +static uc_value_t * uc_lc(uc_vm *vm, size_t nargs) { - const char *str = json_object_get_string(uc_get_arg(0)); - size_t len = str ? strlen(str) : 0; - json_object *rv = NULL; - char *res, *p; + char *str = ucv_to_string(vm, uc_get_arg(0)); + uc_value_t *rv = NULL; + char *p; if (!str) return NULL; - res = p = xalloc(len); + for (p = str; *p; p++) + if (*p >= 'A' && *p <= 'Z') + *p |= 32; - while (*str) - if (*str >= 'A' && *str <= 'Z') - *p++ = 32 + *str++; - else - *p++ = *str++; + rv = ucv_string_new(str); - rv = xjs_new_string_len(res, len); - free(res); + free(str); return rv; } -static json_object * +static uc_value_t * uc_map(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - json_object *func = uc_get_arg(1); - json_object *arr, *rv; + uc_value_t *obj = uc_get_arg(0); + uc_value_t *func = uc_get_arg(1); + uc_value_t *arr, *rv; size_t arridx, arrlen; - if (!json_object_is_type(obj, json_type_array)) + if (ucv_type(obj) != UC_ARRAY) return NULL; - arr = xjs_new_array(); + arr = ucv_array_new(vm); - for (arrlen = json_object_array_length(obj), arridx = 0; arridx < arrlen; arridx++) { + for (arrlen = ucv_array_length(obj), arridx = 0; arridx < arrlen; arridx++) { /* XXX: revisit leaks */ - uc_vm_stack_push(vm, uc_value_get(func)); - uc_vm_stack_push(vm, uc_value_get(json_object_array_get_idx(obj, arridx))); - uc_vm_stack_push(vm, xjs_new_int64(arridx)); - uc_vm_stack_push(vm, uc_value_get(obj)); + uc_vm_stack_push(vm, ucv_get(func)); + uc_vm_stack_push(vm, ucv_get(ucv_array_get(obj, arridx))); + uc_vm_stack_push(vm, ucv_int64_new(arridx)); + uc_vm_stack_push(vm, ucv_get(obj)); if (uc_vm_call(vm, false, 3)) { - uc_value_put(arr); + ucv_put(arr); return NULL; } rv = uc_vm_stack_pop(vm); - json_object_array_add(arr, rv); + ucv_array_push(arr, rv); } return arr; } -static json_object * +static uc_value_t * uc_ord(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - json_object *rv, *pos; + uc_value_t *obj = uc_get_arg(0); + uc_value_t *rv, *pos; const char *str; size_t i, len; int64_t n; - if (!json_object_is_type(obj, json_type_string)) + if (ucv_type(obj) != UC_STRING) return NULL; - str = json_object_get_string(obj); - len = json_object_get_string_len(obj); + str = ucv_string_get(obj); + len = ucv_string_length(obj); if (nargs == 1) - return str[0] ? xjs_new_int64((int64_t)str[0]) : NULL; + return str[0] ? ucv_int64_new((int64_t)str[0]) : NULL; - rv = xjs_new_array(); + rv = ucv_array_new(vm); for (i = 1; i < nargs; i++) { pos = uc_get_arg(i); - if (json_object_is_type(pos, json_type_int)) { - n = json_object_get_int64(pos); + if (ucv_type(pos) == UC_INTEGER) { + n = ucv_int64_get(pos); if (n < 0) n += len; - if (n >= 0 && n < len) { - json_object_array_add(rv, xjs_new_int64((int64_t)str[n])); + if (n >= 0 && (uint64_t)n < len) { + ucv_array_push(rv, ucv_int64_new((int64_t)str[n])); continue; } } - json_object_array_add(rv, NULL); + ucv_array_push(rv, NULL); } return rv; } -static json_object * +static uc_value_t * uc_type(uc_vm *vm, size_t nargs) { - json_object *v = uc_get_arg(0); - uc_objtype_t o = uc_object_type(v); - - switch (o) { - case UC_OBJ_CFUNCTION: - case UC_OBJ_FUNCTION: - case UC_OBJ_CLOSURE: - return xjs_new_string("function"); - - case UC_OBJ_RESSOURCE: - return xjs_new_string("ressource"); - - default: - switch (json_object_get_type(v)) { - case json_type_object: - return xjs_new_string("object"); - - case json_type_array: - return xjs_new_string("array"); + uc_value_t *v = uc_get_arg(0); + uc_type_t t = ucv_type(v); - case json_type_double: - return xjs_new_string("double"); + switch (t) { + case UC_CFUNCTION: + case UC_FUNCTION: + case UC_CLOSURE: + return ucv_string_new("function"); - case json_type_int: - return xjs_new_string("int"); + case UC_INTEGER: + return ucv_string_new("int"); - case json_type_boolean: - return xjs_new_string("bool"); + case UC_BOOLEAN: + return ucv_string_new("bool"); - case json_type_string: - return xjs_new_string("string"); + case UC_NULL: + return NULL; - default: - return NULL; - } + default: + return ucv_string_new(ucv_typename(v)); } } -static json_object * +static uc_value_t * uc_reverse(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - json_object *rv = NULL; + uc_value_t *obj = uc_get_arg(0); + uc_value_t *rv = NULL; size_t len, arridx; const char *str; char *dup, *p; - if (json_object_is_type(obj, json_type_array)) { - rv = xjs_new_array(); + if (ucv_type(obj) == UC_ARRAY) { + rv = ucv_array_new(vm); - for (arridx = json_object_array_length(obj); arridx > 0; arridx--) - json_object_array_add(rv, uc_value_get(json_object_array_get_idx(obj, arridx - 1))); + for (arridx = ucv_array_length(obj); arridx > 0; arridx--) + ucv_array_push(rv, ucv_get(ucv_array_get(obj, arridx - 1))); } - else if (json_object_is_type(obj, json_type_string)) { - len = json_object_get_string_len(obj); - str = json_object_get_string(obj); + else if (ucv_type(obj) == UC_STRING) { + len = ucv_string_length(obj); + str = ucv_string_get(obj); p = dup = xalloc(len + 1); while (len > 0) *p++ = str[--len]; - rv = xjs_new_string(dup); + rv = ucv_string_new(dup); free(dup); } @@ -912,15 +785,15 @@ uc_reverse(uc_vm *vm, size_t nargs) static struct { uc_vm *vm; bool ex; - json_object *fn; + uc_value_t *fn; } sort_ctx; static int sort_fn(const void *k1, const void *k2) { - json_object * const *v1 = k1; - json_object * const *v2 = k2; - json_object *rv; + uc_value_t * const *v1 = k1; + uc_value_t * const *v2 = k2; + uc_value_t *rv; int ret; if (!sort_ctx.fn) @@ -929,9 +802,9 @@ sort_fn(const void *k1, const void *k2) if (sort_ctx.ex) return 0; - uc_vm_stack_push(sort_ctx.vm, uc_value_get(sort_ctx.fn)); - uc_vm_stack_push(sort_ctx.vm, uc_value_get(*v1)); - uc_vm_stack_push(sort_ctx.vm, uc_value_get(*v2)); + uc_vm_stack_push(sort_ctx.vm, ucv_get(sort_ctx.fn)); + uc_vm_stack_push(sort_ctx.vm, ucv_get(*v1)); + uc_vm_stack_push(sort_ctx.vm, ucv_get(*v2)); if (uc_vm_call(sort_ctx.vm, false, 2)) { sort_ctx.ex = true; @@ -943,40 +816,40 @@ sort_fn(const void *k1, const void *k2) ret = !uc_val_is_truish(rv); - uc_value_put(rv); + ucv_put(rv); return ret; } -static json_object * +static uc_value_t * uc_sort(uc_vm *vm, size_t nargs) { - json_object *arr = uc_get_arg(0); - json_object *fn = uc_get_arg(1); + uc_value_t *arr = uc_get_arg(0); + uc_value_t *fn = uc_get_arg(1); - if (!json_object_is_type(arr, json_type_array)) + if (ucv_type(arr) != UC_ARRAY) return NULL; sort_ctx.vm = vm; sort_ctx.fn = fn; - json_object_array_sort(arr, sort_fn); + ucv_array_sort(arr, sort_fn); - return sort_ctx.ex ? NULL : uc_value_get(arr); + return sort_ctx.ex ? NULL : ucv_get(arr); } -static json_object * +static uc_value_t * uc_splice(uc_vm *vm, size_t nargs) { - json_object *arr = uc_get_arg(0); + uc_value_t *arr = uc_get_arg(0); int64_t ofs = uc_cast_int64(uc_get_arg(1)); int64_t remlen = uc_cast_int64(uc_get_arg(2)); size_t arrlen, addlen, idx; - if (!json_object_is_type(arr, json_type_array)) + if (ucv_type(arr) != UC_ARRAY) return NULL; - arrlen = json_object_array_length(arr); + arrlen = ucv_array_length(arr); addlen = nargs; if (addlen == 1) { @@ -991,7 +864,7 @@ uc_splice(uc_vm *vm, size_t nargs) if (ofs < 0) ofs = 0; } - else if (ofs > arrlen) { + else if ((uint64_t)ofs > arrlen) { ofs = arrlen; } @@ -1005,7 +878,7 @@ uc_splice(uc_vm *vm, size_t nargs) if (ofs < 0) ofs = 0; } - else if (ofs > arrlen) { + else if ((uint64_t)ofs > arrlen) { ofs = arrlen; } @@ -1015,71 +888,71 @@ uc_splice(uc_vm *vm, size_t nargs) if (remlen < 0) remlen = 0; } - else if (remlen > arrlen - ofs) { + else if ((uint64_t)remlen > arrlen - ofs) { remlen = arrlen - ofs; } addlen -= 3; } - if (addlen < remlen) { - json_object_array_del_idx(arr, ofs, remlen - addlen); + if (addlen < (uint64_t)remlen) { + ucv_array_delete(arr, ofs, remlen - addlen); } - else if (addlen > remlen) { - for (idx = arrlen; idx > ofs; idx--) - json_object_array_put_idx(arr, idx + addlen - remlen - 1, - uc_value_get(json_object_array_get_idx(arr, idx - 1))); + else if (addlen > (uint64_t)remlen) { + for (idx = arrlen; idx > (uint64_t)ofs; idx--) + ucv_array_set(arr, idx + addlen - remlen - 1, + ucv_get(ucv_array_get(arr, idx - 1))); } for (idx = 0; idx < addlen; idx++) - json_object_array_put_idx(arr, ofs + idx, - uc_value_get(uc_get_arg(3 + idx))); + ucv_array_set(arr, ofs + idx, + ucv_get(uc_get_arg(3 + idx))); - return uc_value_get(arr); + return ucv_get(arr); } -static json_object * +static uc_value_t * uc_split(uc_vm *vm, size_t nargs) { - json_object *str = uc_get_arg(0); - json_object *sep = uc_get_arg(1); - json_object *arr = NULL; + uc_value_t *str = uc_get_arg(0); + uc_value_t *sep = uc_get_arg(1); + uc_value_t *arr = NULL; const char *p, *sepstr, *splitstr; int eflags = 0, res; regmatch_t pmatch; - uc_regexp *re; + uc_regexp_t *re; size_t seplen; - if (!sep || !json_object_is_type(str, json_type_string)) + if (!sep || ucv_type(str) != UC_STRING) return NULL; - arr = xjs_new_array(); - splitstr = json_object_get_string(str); + arr = ucv_array_new(vm); + splitstr = ucv_string_get(str); - if (uc_object_is_type(sep, UC_OBJ_REGEXP)) { - re = uc_object_as_regexp(sep); + if (ucv_type(sep) == UC_REGEXP) { + re = (uc_regexp_t *)sep; while (true) { - res = regexec(&re->re, splitstr, 1, &pmatch, eflags); + res = regexec(&re->regexp, splitstr, 1, &pmatch, eflags); if (res == REG_NOMATCH) break; - json_object_array_add(arr, xjs_new_string_len(splitstr, pmatch.rm_so)); + ucv_array_push(arr, ucv_string_new_length(splitstr, pmatch.rm_so)); splitstr += pmatch.rm_eo; eflags |= REG_NOTBOL; } - json_object_array_add(arr, xjs_new_string(splitstr)); + ucv_array_push(arr, ucv_string_new(splitstr)); } - else if (json_object_is_type(sep, json_type_string)) { - sepstr = json_object_get_string(sep); + else if (ucv_type(sep) == UC_STRING) { + sepstr = ucv_string_get(sep); for (p = splitstr + (*sepstr ? 1 : 0), seplen = strlen(sepstr); *p; p++) { if (!strncmp(p, sepstr, seplen)) { if (*sepstr || p > splitstr) - json_object_array_add(arr, xjs_new_string_len(splitstr, p - splitstr)); + ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr)); splitstr = p + seplen; p = splitstr - (*sepstr ? 1 : 0); @@ -1087,10 +960,10 @@ uc_split(uc_vm *vm, size_t nargs) } if (*splitstr) - json_object_array_add(arr, xjs_new_string_len(splitstr, p - splitstr)); + ucv_array_push(arr, ucv_string_new_length(splitstr, p - splitstr)); } else { - uc_value_put(arr); + ucv_put(arr); return NULL; } @@ -1098,20 +971,20 @@ uc_split(uc_vm *vm, size_t nargs) return arr; } -static json_object * +static uc_value_t * uc_substr(uc_vm *vm, size_t nargs) { - json_object *str = uc_get_arg(0); + uc_value_t *str = uc_get_arg(0); int64_t ofs = uc_cast_int64(uc_get_arg(1)); int64_t sublen = uc_cast_int64(uc_get_arg(2)); const char *p; size_t len; - if (!json_object_is_type(str, json_type_string)) + if (ucv_type(str) != UC_STRING) return NULL; - p = json_object_get_string(str); - len = json_object_get_string_len(str); + p = ucv_string_get(str); + len = ucv_string_length(str); switch (nargs) { case 1: @@ -1127,7 +1000,7 @@ uc_substr(uc_vm *vm, size_t nargs) if (ofs < 0) ofs = 0; } - else if (ofs > len) { + else if ((uint64_t)ofs > len) { ofs = len; } @@ -1142,7 +1015,7 @@ uc_substr(uc_vm *vm, size_t nargs) if (ofs < 0) ofs = 0; } - else if (ofs > len) { + else if ((uint64_t)ofs > len) { ofs = len; } @@ -1152,53 +1025,49 @@ uc_substr(uc_vm *vm, size_t nargs) if (sublen < 0) sublen = 0; } - else if (sublen > len - ofs) { + else if ((uint64_t)sublen > len - ofs) { sublen = len - ofs; } break; } - return xjs_new_string_len(p + ofs, sublen); + return ucv_string_new_length(p + ofs, sublen); } -static json_object * +static uc_value_t * uc_time(uc_vm *vm, size_t nargs) { time_t t = time(NULL); - return xjs_new_int64((int64_t)t); + return ucv_int64_new((int64_t)t); } -static json_object * +static uc_value_t * uc_uc(uc_vm *vm, size_t nargs) { - const char *str = json_object_get_string(uc_get_arg(0)); - size_t len = str ? strlen(str) : 0; - json_object *rv = NULL; - char *res, *p; + char *str = ucv_to_string(vm, uc_get_arg(0)); + uc_value_t *rv = NULL; + char *p; if (!str) return NULL; - res = p = xalloc(len); + for (p = str; *p; p++) + if (*p >= 'a' && *p <= 'z') + *p &= ~32; - while (*str) - if (*str >= 'a' && *str <= 'z') - *p++ = *str++ - 32; - else - *p++ = *str++; + rv = ucv_string_new(str); - rv = xjs_new_string_len(res, len); - free(res); + free(str); return rv; } -static json_object * +static uc_value_t * uc_uchr(uc_vm *vm, size_t nargs) { - json_object *rv = NULL; + uc_value_t *rv = NULL; size_t idx, ulen; char *p, *str; int64_t n; @@ -1231,48 +1100,49 @@ uc_uchr(uc_vm *vm, size_t nargs) break; } - rv = xjs_new_string_len(str, ulen); + rv = ucv_string_new_length(str, ulen); + free(str); return rv; } -static json_object * +static uc_value_t * uc_values(uc_vm *vm, size_t nargs) { - json_object *obj = uc_get_arg(0); - json_object *arr; + uc_value_t *obj = uc_get_arg(0); + uc_value_t *arr; - if (!json_object_is_type(obj, json_type_object)) + if (ucv_type(obj) != UC_OBJECT) return NULL; - arr = xjs_new_array(); + arr = ucv_array_new(vm); - json_object_object_foreach(obj, key, val) { + ucv_object_foreach(obj, key, val) { (void)key; - json_object_array_add(arr, uc_value_get(val)); + ucv_array_push(arr, ucv_get(val)); } return arr; } -static json_object * +static uc_value_t * uc_trim_common(uc_vm *vm, size_t nargs, bool start, bool end) { - json_object *str = uc_get_arg(0); - json_object *chr = uc_get_arg(1); + uc_value_t *str = uc_get_arg(0); + uc_value_t *chr = uc_get_arg(1); const char *p, *c; size_t len; - if (!json_object_is_type(str, json_type_string) || - (chr != NULL && !json_object_is_type(chr, json_type_string))) + if (ucv_type(str) != UC_STRING || + (chr != NULL && ucv_type(chr) != UC_STRING)) return NULL; - c = json_object_get_string(chr); + c = ucv_string_get(chr); c = c ? c : " \t\r\n"; - p = json_object_get_string(str); - len = json_object_get_string_len(str); + p = ucv_string_get(str); + len = ucv_string_length(str); if (start) { while (*p) { @@ -1293,47 +1163,45 @@ uc_trim_common(uc_vm *vm, size_t nargs, bool start, bool end) } } - return xjs_new_string_len(p, len); + return ucv_string_new_length(p, len); } -static json_object * +static uc_value_t * uc_trim(uc_vm *vm, size_t nargs) { return uc_trim_common(vm, nargs, true, true); } -static json_object * +static uc_value_t * uc_ltrim(uc_vm *vm, size_t nargs) { return uc_trim_common(vm, nargs, true, false); } -static json_object * +static uc_value_t * uc_rtrim(uc_vm *vm, size_t nargs) { return uc_trim_common(vm, nargs, false, true); } -static size_t -uc_printf_common(uc_vm *vm, size_t nargs, char **res) +static void +uc_printf_common(uc_vm *vm, size_t nargs, uc_stringbuf_t *buf) { - json_object *fmt = uc_get_arg(0); + uc_value_t *fmt = uc_get_arg(0); char *fp, sfmt[sizeof("%0- 123456789.123456789%")]; - union { const char *s; int64_t n; double d; } arg; + union { char *s; int64_t n; double d; } arg; const char *fstr, *last, *p; - size_t len = 0, argidx = 1; - enum json_type t; - - *res = NULL; + uc_type_t t = UC_NULL; + size_t argidx = 1; - if (json_object_is_type(fmt, json_type_string)) - fstr = json_object_get_string(fmt); + if (ucv_type(fmt) == UC_STRING) + fstr = ucv_string_get(fmt); else fstr = ""; for (last = p = fstr; *p; p++) { if (*p == '%') { - snprintf_append(res, &len, "%s", p - last, last); + ucv_stringbuf_addstr(buf, last, p - last); last = p++; @@ -1405,7 +1273,7 @@ uc_printf_common(uc_vm *vm, size_t nargs, char **res) case 'u': case 'x': case 'X': - t = json_type_int; + t = UC_INTEGER; if (argidx < nargs) arg.n = uc_cast_int64(uc_get_arg(argidx++)); @@ -1420,7 +1288,7 @@ uc_printf_common(uc_vm *vm, size_t nargs, char **res) case 'F': case 'g': case 'G': - t = json_type_double; + t = UC_DOUBLE; if (argidx < nargs) arg.d = uc_cast_double(uc_get_arg(argidx++)); @@ -1430,7 +1298,7 @@ uc_printf_common(uc_vm *vm, size_t nargs, char **res) break; case 'c': - t = json_type_int; + t = UC_INTEGER; if (argidx < nargs) arg.n = uc_cast_int64(uc_get_arg(argidx++)) & 0xff; @@ -1440,33 +1308,31 @@ uc_printf_common(uc_vm *vm, size_t nargs, char **res) break; case 's': - t = json_type_string; + t = UC_STRING; if (argidx < nargs) - arg.s = json_object_get_string(uc_get_arg(argidx++)); + arg.s = ucv_to_string(vm, uc_get_arg(argidx++)); else arg.s = NULL; - arg.s = arg.s ? arg.s : "(null)"; + arg.s = arg.s ? arg.s : xstrdup("(null)"); break; case 'J': - t = json_type_string; + t = UC_STRING; if (argidx < nargs) - arg.s = json_object_to_json_string_ext( - uc_get_arg(argidx++), - JSON_C_TO_STRING_SPACED|JSON_C_TO_STRING_NOSLASHESCAPE|JSON_C_TO_STRING_STRICT); + arg.s = ucv_to_jsonstring(vm, uc_get_arg(argidx++)); else arg.s = NULL; - arg.s = arg.s ? arg.s : "null"; + arg.s = arg.s ? arg.s : xstrdup("null"); break; case '%': - t = json_type_null; + t = UC_NULL; break; @@ -1477,66 +1343,70 @@ uc_printf_common(uc_vm *vm, size_t nargs, char **res) if (fp + 2 >= sfmt + sizeof(sfmt)) goto next; - *fp++ = (t == json_type_string) ? 's' : *p; + *fp++ = (t == UC_STRING) ? 's' : *p; *fp = 0; -#pragma GCC diagnostic ignored "-Wformat-security" - switch (t) { - case json_type_int: sprintf_append(res, &len, sfmt, arg.n); break; - case json_type_double: sprintf_append(res, &len, sfmt, arg.d); break; - case json_type_string: sprintf_append(res, &len, sfmt, arg.s); break; - default: sprintf_append(res, &len, sfmt); break; - } + case UC_INTEGER: + ucv_stringbuf_printf(buf, sfmt, arg.n); + break; -#pragma GCC diagnostic pop + case UC_DOUBLE: + ucv_stringbuf_printf(buf, sfmt, arg.d); + break; + + case UC_STRING: + ucv_stringbuf_printf(buf, sfmt, arg.s); + break; + + default: + ucv_stringbuf_addstr(buf, sfmt, strlen(sfmt)); + break; + } last = p + 1; next: + if (t == UC_STRING) + free(arg.s); + continue; } } - snprintf_append(res, &len, "%s", p - last, last); - - return len; + ucv_stringbuf_addstr(buf, last, p - last); } -static json_object * +static uc_value_t * uc_sprintf(uc_vm *vm, size_t nargs) { - json_object *rv; - char *str = NULL; - size_t len; - - len = uc_printf_common(vm, nargs, &str); - rv = xjs_new_string_len(str, len); + uc_stringbuf_t *buf = ucv_stringbuf_new(); - free(str); + uc_printf_common(vm, nargs, buf); - return rv; + return ucv_stringbuf_finish(buf); } -static json_object * +static uc_value_t * uc_printf(uc_vm *vm, size_t nargs) { - char *str = NULL; + uc_stringbuf_t *buf = xprintbuf_new(); size_t len; - len = uc_printf_common(vm, nargs, &str); - len = fwrite(str, 1, len, stdout); + uc_printf_common(vm, nargs, buf); - free(str); + len = fwrite(buf->buf, 1, printbuf_length(buf), stdout); - return xjs_new_int64(len); + printbuf_free(buf); + + return ucv_int64_new(len); } static bool -uc_require_so(uc_vm *vm, const char *path, json_object **res) +uc_require_so(uc_vm *vm, const char *path, uc_value_t **res) { - void (*init)(const uc_ops *, uc_prototype *); - uc_prototype *scope; + void (*init)(uc_value_t *); + uc_value_t *scope; struct stat st; void *dlh; @@ -1562,22 +1432,22 @@ uc_require_so(uc_vm *vm, const char *path, json_object **res) return true; } - scope = uc_prototype_new(NULL); + scope = ucv_object_new(vm); - init(&uc, scope); + init(scope); - *res = scope->header.jso; + *res = scope; return true; } static bool -uc_require_ucode(uc_vm *vm, const char *path, uc_prototype *scope, json_object **res) +uc_require_ucode(uc_vm *vm, const char *path, uc_value_t *scope, uc_value_t **res) { uc_exception_type_t extype; - uc_prototype *prev_scope; - uc_function *function; - uc_closure *closure; + uc_function_t *function; + uc_value_t *prev_scope; + uc_value_t *closure; uc_source *source; struct stat st; char *err; @@ -1606,9 +1476,9 @@ uc_require_ucode(uc_vm *vm, const char *path, uc_prototype *scope, json_object * return true; } - closure = uc_closure_new(function, false); + closure = ucv_closure_new(vm, function, false); - uc_vm_stack_push(vm, closure->header.jso); + uc_vm_stack_push(vm, closure); prev_scope = vm->globals; vm->globals = scope ? scope : prev_scope; @@ -1626,11 +1496,10 @@ uc_require_ucode(uc_vm *vm, const char *path, uc_prototype *scope, json_object * } static bool -uc_require_path(uc_vm *vm, const char *path_template, const char *name, json_object **res) +uc_require_path(uc_vm *vm, const char *path_template, const char *name, uc_value_t **res) { + uc_stringbuf_t *buf = xprintbuf_new(); const char *p, *q, *last; - char *path = NULL; - size_t plen = 0; bool rv = false; *res = NULL; @@ -1640,12 +1509,16 @@ uc_require_path(uc_vm *vm, const char *path_template, const char *name, json_obj if (!p) goto invalid; - snprintf_append(&path, &plen, "%s", p - path_template, path_template); + ucv_stringbuf_addstr(buf, path_template, p - path_template); for (q = last = name;; q++) { if (*q == '.' || *q == '\0') { - snprintf_append(&path, &plen, "%s", q - last, last); - sprintf_append(&path, &plen, "%s", *q ? "/" : ++p); + ucv_stringbuf_addstr(buf, last, q - last); + + if (*q) + ucv_stringbuf_append(buf, "/"); + else + ucv_stringbuf_addstr(buf, p + 1, strlen(p + 1)); if (*q == '\0') break; @@ -1657,45 +1530,45 @@ uc_require_path(uc_vm *vm, const char *path_template, const char *name, json_obj } } - if (!strcmp(p, ".so")) - rv = uc_require_so(vm, path, res); - else if (!strcmp(p, ".uc")) - rv = uc_require_ucode(vm, path, NULL, res); + if (!strcmp(p + 1, ".so")) + rv = uc_require_so(vm, buf->buf, res); + else if (!strcmp(p + 1, ".uc")) + rv = uc_require_ucode(vm, buf->buf, NULL, res); invalid: - free(path); + printbuf_free(buf); return rv; } -static json_object * +static uc_value_t * uc_require(uc_vm *vm, size_t nargs) { - json_object *val = uc_get_arg(0); - json_object *search, *se, *res; + uc_value_t *val = uc_get_arg(0); + uc_value_t *search, *se, *res; size_t arridx, arrlen; const char *name; - if (!json_object_is_type(val, json_type_string)) + if (ucv_type(val) != UC_STRING) return NULL; - name = json_object_get_string(val); - search = uc_prototype_lookup(vm->globals, "REQUIRE_SEARCH_PATH"); + name = ucv_string_get(val); + search = ucv_property_get(vm->globals, "REQUIRE_SEARCH_PATH"); - if (!json_object_is_type(search, json_type_array)) { + if (ucv_type(search) != UC_ARRAY) { uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "Global require search path not set"); return NULL; } - for (arridx = 0, arrlen = json_object_array_length(search); arridx < arrlen; arridx++) { - se = json_object_array_get_idx(search, arridx); + for (arridx = 0, arrlen = ucv_array_length(search); arridx < arrlen; arridx++) { + se = ucv_array_get(search, arridx); - if (!json_object_is_type(se, json_type_string)) + if (ucv_type(se) != UC_STRING) continue; - if (uc_require_path(vm, json_object_get_string(se), name, &res)) + if (uc_require_path(vm, ucv_string_get(se), name, &res)) return res; } @@ -1705,11 +1578,11 @@ uc_require(uc_vm *vm, size_t nargs) return NULL; } -static json_object * +static uc_value_t * uc_iptoarr(uc_vm *vm, size_t nargs) { - json_object *ip = uc_get_arg(0); - json_object *res; + uc_value_t *ip = uc_get_arg(0); + uc_value_t *res; union { uint8_t u8[4]; struct in_addr in; @@ -1717,24 +1590,24 @@ uc_iptoarr(uc_vm *vm, size_t nargs) } a; int i; - if (!json_object_is_type(ip, json_type_string)) + if (ucv_type(ip) != UC_STRING) return NULL; - if (inet_pton(AF_INET6, json_object_get_string(ip), &a)) { - res = xjs_new_array(); + if (inet_pton(AF_INET6, ucv_string_get(ip), &a)) { + res = ucv_array_new(vm); for (i = 0; i < 16; i++) - json_object_array_add(res, xjs_new_int64(a.in6.s6_addr[i])); + ucv_array_push(res, ucv_int64_new(a.in6.s6_addr[i])); return res; } - else if (inet_pton(AF_INET, json_object_get_string(ip), &a)) { - res = xjs_new_array(); + else if (inet_pton(AF_INET, ucv_string_get(ip), &a)) { + res = ucv_array_new(vm); - json_object_array_add(res, xjs_new_int64(a.u8[0])); - json_object_array_add(res, xjs_new_int64(a.u8[1])); - json_object_array_add(res, xjs_new_int64(a.u8[2])); - json_object_array_add(res, xjs_new_int64(a.u8[3])); + ucv_array_push(res, ucv_int64_new(a.u8[0])); + ucv_array_push(res, ucv_int64_new(a.u8[1])); + ucv_array_push(res, ucv_int64_new(a.u8[2])); + ucv_array_push(res, ucv_int64_new(a.u8[3])); return res; } @@ -1743,14 +1616,14 @@ uc_iptoarr(uc_vm *vm, size_t nargs) } static int -check_byte(json_object *v) +check_byte(uc_value_t *v) { int n; - if (!json_object_is_type(v, json_type_int)) + if (ucv_type(v) != UC_INTEGER) return -1; - n = json_object_get_int(v); + n = ucv_int64_get(v); if (n < 0 || n > 255) return -1; @@ -1758,10 +1631,10 @@ check_byte(json_object *v) return n; } -static json_object * +static uc_value_t * uc_arrtoip(uc_vm *vm, size_t nargs) { - json_object *arr = uc_get_arg(0); + uc_value_t *arr = uc_get_arg(0); union { uint8_t u8[4]; struct in6_addr in6; @@ -1769,13 +1642,13 @@ uc_arrtoip(uc_vm *vm, size_t nargs) char buf[INET6_ADDRSTRLEN]; int i, n; - if (!json_object_is_type(arr, json_type_array)) + if (ucv_type(arr) != UC_ARRAY) return NULL; - switch (json_object_array_length(arr)) { + switch (ucv_array_length(arr)) { case 4: for (i = 0; i < 4; i++) { - n = check_byte(json_object_array_get_idx(arr, i)); + n = check_byte(ucv_array_get(arr, i)); if (n < 0) return NULL; @@ -1785,11 +1658,11 @@ uc_arrtoip(uc_vm *vm, size_t nargs) inet_ntop(AF_INET, &a, buf, sizeof(buf)); - return xjs_new_string(buf); + return ucv_string_new(buf); case 16: for (i = 0; i < 16; i++) { - n = check_byte(json_object_array_get_idx(arr, i)); + n = check_byte(ucv_array_get(arr, i)); if (n < 0) return NULL; @@ -1799,49 +1672,51 @@ uc_arrtoip(uc_vm *vm, size_t nargs) inet_ntop(AF_INET6, &a, buf, sizeof(buf)); - return xjs_new_string(buf); + return ucv_string_new(buf); default: return NULL; } } -static json_object * +static uc_value_t * uc_match(uc_vm *vm, size_t nargs) { - json_object *subject = uc_get_arg(0); - json_object *pattern = uc_get_arg(1); - json_object *rv = NULL, *m; - int eflags = 0, res, i; + uc_value_t *subject = uc_get_arg(0); + uc_value_t *pattern = uc_get_arg(1); + uc_value_t *rv = NULL, *m; regmatch_t pmatch[10]; - uc_regexp *re; - const char *p; + int eflags = 0, res; + uc_regexp_t *re; + bool freeable; + char *p; + size_t i; - if (!uc_object_is_type(pattern, UC_OBJ_REGEXP) || !subject) + if (ucv_type(pattern) != UC_REGEXP || !subject) return NULL; - p = json_object_get_string(subject); - re = uc_object_as_regexp(pattern); + p = uc_cast_string(vm, &subject, &freeable); + re = (uc_regexp_t *)pattern; while (true) { - res = regexec(&re->re, p, ARRAY_SIZE(pmatch), pmatch, eflags); + res = regexec(&re->regexp, p, ARRAY_SIZE(pmatch), pmatch, eflags); if (res == REG_NOMATCH) break; - m = xjs_new_array(); + m = ucv_array_new(vm); for (i = 0; i < ARRAY_SIZE(pmatch) && pmatch[i].rm_so != -1; i++) { - json_object_array_add(m, - xjs_new_string_len(p + pmatch[i].rm_so, - pmatch[i].rm_eo - pmatch[i].rm_so)); + ucv_array_push(m, + ucv_string_new_length(p + pmatch[i].rm_so, + pmatch[i].rm_eo - pmatch[i].rm_so)); } if (re->global) { if (!rv) - rv = xjs_new_array(); + rv = ucv_array_new(vm); - json_object_array_add(rv, m); + ucv_array_push(rv, m); p += pmatch[0].rm_eo; eflags |= REG_NOTBOL; @@ -1852,24 +1727,27 @@ uc_match(uc_vm *vm, size_t nargs) } } + if (freeable) + free(p); + return rv; } -static json_object * -uc_replace_cb(uc_vm *vm, json_object *func, +static uc_value_t * +uc_replace_cb(uc_vm *vm, uc_value_t *func, const char *subject, regmatch_t *pmatch, size_t plen, - char **sp, size_t *sl) + uc_stringbuf_t *resbuf) { - json_object *rv; + uc_value_t *rv; size_t i; /* XXX: revisit leaks */ - uc_vm_stack_push(vm, uc_value_get(func)); + uc_vm_stack_push(vm, ucv_get(func)); for (i = 0; i < plen && pmatch[i].rm_so != -1; i++) { uc_vm_stack_push(vm, - xjs_new_string_len(subject + pmatch[i].rm_so, - pmatch[i].rm_eo - pmatch[i].rm_so)); + ucv_string_new_length(subject + pmatch[i].rm_so, + pmatch[i].rm_eo - pmatch[i].rm_so)); } if (uc_vm_call(vm, false, i)) @@ -1877,39 +1755,42 @@ uc_replace_cb(uc_vm *vm, json_object *func, rv = uc_vm_stack_pop(vm); - sprintf_append(sp, sl, "%s", rv ? json_object_get_string(rv) : "null"); + ucv_to_stringbuf(vm, resbuf, rv, false); - uc_value_put(rv); + ucv_put(rv); return NULL; } static void -uc_replace_str(uc_vm *vm, json_object *str, +uc_replace_str(uc_vm *vm, uc_value_t *str, const char *subject, regmatch_t *pmatch, size_t plen, - char **sp, size_t *sl) + uc_stringbuf_t *resbuf) { - const char *p, *r = str ? json_object_get_string(str) : "null"; bool esc = false; - int i; + char *p, *r; + uint8_t i; - for (p = r; *p; p++) { + for (p = r = ucv_to_string(vm, str); *p; p++) { if (esc) { switch (*p) { case '&': if (pmatch[0].rm_so != -1) - snprintf_append(sp, sl, "%s", pmatch[0].rm_eo - pmatch[0].rm_so, - subject + pmatch[0].rm_so); + ucv_stringbuf_addstr(resbuf, + subject + pmatch[0].rm_so, + pmatch[0].rm_eo - pmatch[0].rm_so); break; case '`': if (pmatch[0].rm_so != -1) - snprintf_append(sp, sl, "%s", pmatch[0].rm_so, subject); + ucv_stringbuf_addstr(resbuf, subject, pmatch[0].rm_so); break; case '\'': if (pmatch[0].rm_so != -1) - sprintf_append(sp, sl, "%s", subject + pmatch[0].rm_eo); + ucv_stringbuf_addstr(resbuf, + subject + pmatch[0].rm_eo, + strlen(subject + pmatch[0].rm_eo)); break; case '1': @@ -1922,19 +1803,24 @@ uc_replace_str(uc_vm *vm, json_object *str, case '8': case '9': i = *p - '0'; - if (i < plen && pmatch[i].rm_so != -1) - snprintf_append(sp, sl, "%s", pmatch[i].rm_eo - pmatch[i].rm_so, - subject + pmatch[i].rm_so); - else - sprintf_append(sp, sl, "$%c", *p); + if (i < plen && pmatch[i].rm_so != -1) { + ucv_stringbuf_addstr(resbuf, + subject + pmatch[i].rm_so, + pmatch[i].rm_eo - pmatch[i].rm_so); + } + else { + ucv_stringbuf_append(resbuf, "$"); + ucv_stringbuf_addstr(resbuf, p, 1); + } break; case '$': - sprintf_append(sp, sl, "$"); + ucv_stringbuf_append(resbuf, "$"); break; default: - sprintf_append(sp, sl, "$%c", *p); + ucv_stringbuf_append(resbuf, "$"); + ucv_stringbuf_addstr(resbuf, p, 1); } esc = false; @@ -1943,51 +1829,58 @@ uc_replace_str(uc_vm *vm, json_object *str, esc = true; } else { - sprintf_append(sp, sl, "%c", *p); + ucv_stringbuf_addstr(resbuf, p, 1); } } + + free(r); } -static json_object * +static uc_value_t * uc_replace(uc_vm *vm, size_t nargs) { - json_object *subject = uc_get_arg(0); - json_object *pattern = uc_get_arg(1); - json_object *replace = uc_get_arg(2); - json_object *rv = NULL; - const char *sb, *p, *l; + char *sb = NULL, *pt = NULL, *p, *l; + uc_value_t *subject = uc_get_arg(0); + uc_value_t *pattern = uc_get_arg(1); + uc_value_t *replace = uc_get_arg(2); + bool sb_freeable, pt_freeable; + uc_value_t *rv = NULL; + uc_stringbuf_t *resbuf; regmatch_t pmatch[10]; int eflags = 0, res; - size_t sl = 0, pl; - char *sp = NULL; - uc_regexp *re; + uc_regexp_t *re; + size_t pl; if (!pattern || !subject || !replace) return NULL; - if (uc_object_is_type(pattern, UC_OBJ_REGEXP)) { - p = json_object_get_string(subject); - re = uc_object_as_regexp(pattern); + sb = uc_cast_string(vm, &subject, &sb_freeable); + resbuf = ucv_stringbuf_new(); + + if (ucv_type(pattern) == UC_REGEXP) { + re = (uc_regexp_t *)pattern; + p = sb; while (true) { - res = regexec(&re->re, p, ARRAY_SIZE(pmatch), pmatch, eflags); + res = regexec(&re->regexp, p, ARRAY_SIZE(pmatch), pmatch, eflags); if (res == REG_NOMATCH) break; - snprintf_append(&sp, &sl, "%s", pmatch[0].rm_so, p); + ucv_stringbuf_addstr(resbuf, p, pmatch[0].rm_so); - if (uc_object_is_callable(replace)) { - rv = uc_replace_cb(vm, replace, p, pmatch, ARRAY_SIZE(pmatch), &sp, &sl); + if (ucv_is_callable(replace)) { + rv = uc_replace_cb(vm, replace, p, pmatch, ARRAY_SIZE(pmatch), resbuf); if (rv) { - free(sp); + if (sb_freeable) + free(sb); return rv; } } else { - uc_replace_str(vm, replace, p, pmatch, ARRAY_SIZE(pmatch), &sp, &sl); + uc_replace_str(vm, replace, p, pmatch, ARRAY_SIZE(pmatch), resbuf); } p += pmatch[0].rm_eo; @@ -1998,57 +1891,64 @@ uc_replace(uc_vm *vm, size_t nargs) break; } - sprintf_append(&sp, &sl, "%s", p); + ucv_stringbuf_addstr(resbuf, p, strlen(p)); } else { - sb = json_object_get_string(subject); - p = json_object_get_string(pattern); - pl = strlen(p); + pt = uc_cast_string(vm, &pattern, &pt_freeable); + pl = strlen(pt); - for (l = sb; *sb; sb++) { - if (!strncmp(sb, p, pl)) { - snprintf_append(&sp, &sl, "%s", sb - l, l); + for (l = p = sb; *p; p++) { + if (!strncmp(p, pt, pl)) { + ucv_stringbuf_addstr(resbuf, l, p - l); - pmatch[0].rm_so = sb - l; + pmatch[0].rm_so = p - l; pmatch[0].rm_eo = pmatch[0].rm_so + pl; - if (uc_object_is_callable(replace)) { - rv = uc_replace_cb(vm, replace, l, pmatch, 1, &sp, &sl); + if (ucv_is_callable(replace)) { + rv = uc_replace_cb(vm, replace, l, pmatch, 1, resbuf); if (rv) { - free(sp); + if (sb_freeable) + free(sb); + + if (pt_freeable) + free(pt); return rv; } } else { - uc_replace_str(vm, replace, l, pmatch, 1, &sp, &sl); + uc_replace_str(vm, replace, l, pmatch, 1, resbuf); } - l = sb + pl; - sb += pl - 1; + l = p + pl; + p += pl - 1; } } - sprintf_append(&sp, &sl, "%s", l); + ucv_stringbuf_addstr(resbuf, l, strlen(l)); + + if (pt_freeable) + free(pt); } - rv = xjs_new_string_len(sp, sl); - free(sp); + if (sb_freeable) + free(sb); - return rv; + return ucv_stringbuf_finish(resbuf); } -static json_object * +static uc_value_t * uc_json(uc_vm *vm, size_t nargs) { - json_object *rv, *src = uc_get_arg(0); + uc_value_t *rv, *src = uc_get_arg(0); struct json_tokener *tok = NULL; enum json_tokener_error err; + json_object *jso; const char *str; size_t len; - if (!json_object_is_type(src, json_type_string)) { + if (ucv_type(src) != UC_STRING) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed value is not a string"); @@ -2056,24 +1956,24 @@ uc_json(uc_vm *vm, size_t nargs) } tok = xjs_new_tokener(); - str = json_object_get_string(src); - len = json_object_get_string_len(src); + str = ucv_string_get(src); + len = ucv_string_length(src); /* NB: the len + 1 here is intentional to pass the terminating \0 byte * to the json-c parser. This is required to work-around upstream * issue #681 */ - rv = json_tokener_parse_ex(tok, str, len + 1); + jso = json_tokener_parse_ex(tok, str, len + 1); err = json_tokener_get_error(tok); if (err == json_tokener_continue) { - uc_value_put(rv); + json_object_put(jso); uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "Unexpected end of string in JSON data"); return NULL; } else if (err != json_tokener_success) { - uc_value_put(rv); + json_object_put(jso); uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "Failed to parse JSON string: %s", json_tokener_error_desc(err)); @@ -2081,7 +1981,7 @@ uc_json(uc_vm *vm, size_t nargs) return NULL; } else if (json_tokener_get_parse_end(tok) < len) { - uc_value_put(rv); + json_object_put(jso); uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "Trailing garbage after JSON data"); @@ -2090,6 +1990,10 @@ uc_json(uc_vm *vm, size_t nargs) json_tokener_free(tok); + rv = ucv_from_json(vm, jso); + + json_object_put(jso); + return rv; } @@ -2126,26 +2030,24 @@ include_path(const char *curpath, const char *incpath) return dup; } -static json_object * +static uc_value_t * uc_include(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); - json_object *scope = uc_get_arg(1); - json_object *rv = NULL; - uc_closure *closure = NULL; - uc_prototype *sc; - bool put = false; + uc_value_t *path = uc_get_arg(0); + uc_value_t *scope = uc_get_arg(1); + uc_value_t *rv = NULL, *sc = NULL; + uc_closure_t *closure = NULL; size_t i; char *p; - if (!json_object_is_type(path, json_type_string)) { + if (ucv_type(path) != UC_STRING) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed filename is not a string"); return NULL; } - if (scope && !json_object_is_type(scope, json_type_object)) { + if (scope && ucv_type(scope) != UC_OBJECT) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed scope value is not an object"); @@ -2163,7 +2065,7 @@ uc_include(uc_vm *vm, size_t nargs) if (!closure) return NULL; - p = include_path(closure->function->source->filename, json_object_get_string(path)); + p = include_path(closure->function->source->filename, ucv_string_get(path)); if (!p) { uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, @@ -2172,68 +2074,67 @@ uc_include(uc_vm *vm, size_t nargs) return NULL; } - if (uc_object_is_type(scope, UC_OBJ_PROTOTYPE)) { - sc = uc_object_as_prototype(scope); + if (ucv_prototype_get(scope)) { + sc = ucv_get(scope); } else if (scope) { - sc = uc_prototype_new(vm->globals); - put = true; + sc = ucv_object_new(vm); + + ucv_object_foreach(scope, key, val) + ucv_object_add(sc, key, ucv_get(val)); - json_object_object_foreach(scope, key, val) - json_object_object_add(sc->header.jso, key, uc_value_get(val)); + ucv_prototype_set(sc, ucv_get(vm->globals)); } else { - sc = vm->globals; + sc = ucv_get(vm->globals); } if (uc_require_ucode(vm, p, sc, &rv)) - uc_value_put(rv); + ucv_put(rv); + ucv_put(sc); free(p); - if (put) - uc_value_put(sc->header.jso); - return NULL; } -static json_object * +static uc_value_t * uc_warn(uc_vm *vm, size_t nargs) { return uc_print_common(vm, nargs, stderr); } -static json_object * +static uc_value_t * uc_system(uc_vm *vm, size_t nargs) { - json_object *cmdline = uc_get_arg(0); - json_object *timeout = uc_get_arg(1); - const char **arglist, *fn, *s; + uc_value_t *cmdline = uc_get_arg(0); + uc_value_t *timeout = uc_get_arg(1); + const char **arglist, *fn; sigset_t sigmask, sigomask; struct timespec ts; + size_t i, len; int64_t tms; - int rc, len; pid_t cld; - size_t i; + int rc; - if (timeout && (!json_object_is_type(timeout, json_type_int) || json_object_get_int64(timeout) < 0)) { + if (timeout && (ucv_type(timeout) != UC_INTEGER || ucv_int64_get(timeout) < 0)) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid timeout specified"); return NULL; } - switch (json_object_get_type(cmdline)) { - case json_type_string: + switch (ucv_type(cmdline)) { + case UC_STRING: arglist = xalloc(sizeof(*arglist) * 4); arglist[0] = "/bin/sh"; arglist[1] = "-c"; - arglist[2] = json_object_get_string(cmdline); + arglist[2] = ucv_string_get(cmdline); arglist[3] = NULL; break; - case json_type_array: - len = json_object_array_length(cmdline); + case UC_ARRAY: + len = ucv_array_length(cmdline); if (len == 0) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, @@ -2244,10 +2145,8 @@ uc_system(uc_vm *vm, size_t nargs) arglist = xalloc(sizeof(*arglist) * (len + 1)); - for (i = 0; i < len; i++) { - s = json_object_get_string(json_object_array_get_idx(cmdline, i)); - arglist[i] = s ? s : "null"; - } + for (i = 0; i < len; i++) + arglist[i] = ucv_to_string(vm, ucv_array_get(cmdline, i)); arglist[i] = NULL; @@ -2260,7 +2159,7 @@ uc_system(uc_vm *vm, size_t nargs) return NULL; } - tms = timeout ? json_object_get_int64(timeout) : 0; + tms = timeout ? ucv_int64_get(timeout) : 0; if (tms > 0) { sigemptyset(&sigmask); @@ -2315,14 +2214,17 @@ uc_system(uc_vm *vm, size_t nargs) if (tms > 0) sigprocmask(SIG_SETMASK, &sigomask, NULL); + for (i = 0; arglist[i]; i++) + free((char *)arglist[i]); + free(arglist); if (WIFEXITED(rc)) - return xjs_new_int64(WEXITSTATUS(rc)); + return ucv_int64_new(WEXITSTATUS(rc)); else if (WIFSIGNALED(rc)) - return xjs_new_int64(-WTERMSIG(rc)); + return ucv_int64_new(-WTERMSIG(rc)); else if (WIFSTOPPED(rc)) - return xjs_new_int64(-WSTOPSIG(rc)); + return ucv_int64_new(-WSTOPSIG(rc)); return NULL; } @@ -2331,6 +2233,9 @@ fail: if (tms > 0) sigprocmask(SIG_SETMASK, &sigomask, NULL); + for (i = 0; arglist[i]; i++) + free((char *)arglist[i]); + free(arglist); uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, @@ -2339,95 +2244,61 @@ fail: return NULL; } -static json_object * +static uc_value_t * uc_trace(uc_vm *vm, size_t nargs) { - json_object *level = uc_get_arg(0); + uc_value_t *level = uc_get_arg(0); uint8_t prev_level; - if (!json_object_is_type(level, json_type_int)) { + if (ucv_type(level) != UC_INTEGER) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid level specified"); return NULL; } prev_level = vm->trace; - vm->trace = json_object_get_int64(level); + vm->trace = ucv_int64_get(level); - return xjs_new_int64(prev_level); + return ucv_int64_new(prev_level); } -static json_object * +static uc_value_t * uc_proto(uc_vm *vm, size_t nargs) { - json_object *val = uc_get_arg(0); - json_object *proto = NULL; - uc_prototype *p, *ref; - - if (nargs < 2) { - switch (uc_object_type(val)) { - case UC_OBJ_PROTOTYPE: - p = uc_object_as_prototype(val)->parent; + uc_value_t *val = uc_get_arg(0); + uc_value_t *proto = NULL; - return p ? uc_value_get(p->header.jso) : NULL; - - case UC_OBJ_RESSOURCE: - p = uc_ressource_prototype(val); - - return p ? uc_value_get(p->header.jso) : NULL; - - default: - return NULL; - } - } + if (nargs < 2) + return ucv_get(ucv_prototype_get(val)); proto = uc_get_arg(1); - switch (uc_object_type(proto)) { - case UC_OBJ_PROTOTYPE: - p = uc_object_as_prototype(proto); - break; - - case UC_OBJ_RESSOURCE: - p = uc_ressource_prototype(proto); - break; - - default: - switch (json_object_get_type(proto)) { - case json_type_object: - p = uc_protoref_new(proto, NULL); - break; - - default: - uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed value is neither a prototype, ressource or object"); - - return NULL; - } - } + if (!ucv_prototype_set(val, proto)) + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Passed value is neither a prototype, ressource or object"); - ref = uc_protoref_new(val, p); + ucv_get(proto); - return ref ? uc_value_get(ref->header.jso) : NULL; + return ucv_get(val); } -static json_object * +static uc_value_t * uc_sleep(uc_vm *vm, size_t nargs) { - json_object *duration = uc_get_arg(0); + uc_value_t *duration = uc_get_arg(0); struct timeval tv; int64_t ms; ms = uc_cast_int64(duration); if (errno != 0 || ms <= 0) - return xjs_new_boolean(false); + return ucv_boolean_new(false); tv.tv_sec = ms / 1000; tv.tv_usec = (ms % 1000) * 1000; select(0, NULL, NULL, NULL, &tv); - return xjs_new_boolean(true); + return ucv_boolean_new(true); } static const uc_cfunction_list functions[] = { @@ -2484,7 +2355,7 @@ static const uc_cfunction_list functions[] = { void -uc_lib_init(uc_prototype *scope) +uc_lib_init(uc_value_t *scope) { uc_add_proto_functions(scope, functions); } diff --git a/lib.h b/lib.h index c14d11d..aad9a9d 100644 --- a/lib.h +++ b/lib.h @@ -19,185 +19,106 @@ #include "vm.h" #include "lexer.h" -#include "object.h" typedef struct { const char *name; - uc_cfn_ptr func; + uc_cfn_ptr_t func; } uc_cfunction_list; -typedef struct { - /* value operations */ - struct { - uc_prototype *(*proto)(uc_prototype *); - uc_cfunction *(*cfunc)(const char *, uc_cfn_ptr); - json_object *(*dbl)(double); - uc_regexp *(*regexp)(const char *, bool, bool, bool, char **); - uc_ressource *(*ressource)(json_object *, uc_ressource_type *, void *); - enum json_type (*tonumber)(json_object *, int64_t *, double *); - } value; - - /* ressource operations */ - struct { - uc_ressource_type *(*define)(const char *, uc_prototype *, void (*)(void *)); - uc_ressource *(*create)(json_object *, uc_ressource_type *, void *); - void **(*data)(json_object *, const char *); - uc_prototype *(*proto)(json_object *); - } ressource; - - /* VM operations */ - struct { - uc_exception_type_t (*call)(uc_vm *, bool, size_t); - json_object *(*peek)(uc_vm *, size_t); - json_object *(*pop)(uc_vm *); - void (*push)(uc_vm *, json_object *); - void (*raise)(uc_vm *, uc_exception_type_t, const char *, ...); - } vm; -} uc_ops; - -extern const uc_ops uc; - -void uc_lib_init(uc_prototype *scope); - -void format_source_context(char **msg, size_t *msglen, uc_source *src, size_t off, bool compact); -void format_error_context(char **msg, size_t *msglen, uc_source *src, json_object *stacktrace, size_t off); +void uc_lib_init(uc_value_t *scope); + +bool format_source_context(uc_stringbuf_t *buf, uc_source *src, size_t off, bool compact); +bool format_error_context(uc_stringbuf_t *buf, uc_source *src, uc_value_t *stacktrace, size_t off); /* vm helper */ static inline void * -_uc_get_self(const uc_ops *ops, uc_vm *vm, const char *expected_type) +_uc_get_self(uc_vm *vm, const char *expected_type) { - return ops->ressource.data(vm->callframes.entries[vm->callframes.count - 1].ctx, expected_type); + return ucv_ressource_dataptr(vm->callframes.entries[vm->callframes.count - 1].ctx, expected_type); } -#define uc_get_self(...) _uc_get_self(ops, vm, __VA_ARGS__) +#define uc_get_self(...) _uc_get_self(vm, __VA_ARGS__) -static inline json_object * -_uc_get_arg(const uc_ops *ops, uc_vm *vm, size_t nargs, size_t n) +static inline uc_value_t * +_uc_get_arg(uc_vm *vm, size_t nargs, size_t n) { if (n >= nargs) return NULL; - return ops->vm.peek(vm, nargs - n - 1); + return uc_vm_stack_peek(vm, nargs - n - 1); } -#define uc_get_arg(...) _uc_get_arg(ops, vm, nargs, __VA_ARGS__) +#define uc_get_arg(...) _uc_get_arg(vm, nargs, __VA_ARGS__) -#define uc_call(nargs) ops->vm.call(vm, false, nargs) -#define uc_push_val(val) ops->vm.push(vm, val) -#define uc_pop_val() ops->vm.pop(vm) +#define uc_call(nargs) uc_vm_call(vm, false, nargs) +#define uc_push_val(val) uc_vm_stack_push(vm, val) +#define uc_pop_val() uc_vm_stack_pop(vm) /* value helper */ -static inline json_object * -_uc_alloc_proto(const uc_ops *ops, uc_prototype *parent) -{ - return ops->value.proto(parent)->header.jso; -} - -static inline json_object * -_uc_alloc_cfunc(const uc_ops *ops, const char *name, uc_cfn_ptr fptr) -{ - return ops->value.cfunc(name, fptr)->header.jso; -} - -static inline json_object * -_uc_alloc_double(const uc_ops *ops, double dbl) -{ - return ops->value.dbl(dbl); -} - -static inline json_object * -_uc_alloc_regexp(const uc_ops *ops, const char *pattern, bool global, bool icase, bool newline, char **errp) +static inline uc_value_t * +uc_alloc_ressource(uc_ressource_type_t *type, void *data) { - uc_regexp *re = ops->value.regexp(pattern, global, icase, newline, errp); - - return re ? re->header.jso : NULL; -} - -static inline json_object * -_uc_alloc_ressource(const uc_ops *ops, uc_ressource_type *type, void *data) -{ - uc_ressource *res = ops->value.ressource(xjs_new_object(), type, data); - - return res ? res->header.jso : NULL; + return ucv_ressource_new(type, data); } -#define uc_alloc_proto(...) _uc_alloc_proto(ops, __VA_ARGS__) -#define uc_alloc_cfunc(...) _uc_alloc_cfunc(ops, __VA_ARGS__) -#define uc_alloc_double(...) _uc_alloc_double(ops, __VA_ARGS__) -#define uc_alloc_regexp(...) _uc_alloc_regexp(ops, __VA_ARGS__) -#define uc_alloc_ressource(...) _uc_alloc_ressource(ops, __VA_ARGS__) - -static inline json_type -_uc_to_number(const uc_ops *ops, json_object *v, int64_t *n, double *d) +static inline uc_type_t +uc_to_number(uc_value_t *v, int64_t *n, double *d) { - return ops->value.tonumber(v, n, d); + return uc_cast_number(v, n, d); } static inline double -_uc_to_double(const uc_ops *ops, json_object *v) +uc_to_double(uc_value_t *v) { int64_t n; double d; - return (ops->value.tonumber(v, &n, &d) == json_type_double) ? d : (double)n; + return (uc_cast_number(v, &n, &d) == UC_DOUBLE) ? d : (double)n; } static inline int64_t -_uc_to_int64(const uc_ops *ops, json_object *v) +uc_to_int64(uc_value_t *v) { int64_t n; double d; - return (ops->value.tonumber(v, &n, &d) == json_type_double) ? (int64_t)d : n; + return (uc_cast_number(v, &n, &d) == UC_DOUBLE) ? (int64_t)d : n; } -#define uc_to_number(...) _uc_to_number(ops, __VA_ARGS__) -#define uc_to_double(...) _uc_to_double(ops, __VA_ARGS__) -#define uc_to_int64(...) _uc_to_int64(ops, __VA_ARGS__) - /* ressource type helper */ -static inline uc_ressource_type * -_uc_declare_type(const uc_ops *ops, const char *name, const uc_cfunction_list *list, size_t len, void (*freefn)(void *)) +static inline uc_ressource_type_t * +_uc_declare_type(const char *name, const uc_cfunction_list *list, size_t len, void (*freefn)(void *)) { - uc_prototype *proto = ops->value.proto(NULL); + uc_value_t *proto = ucv_object_new(NULL); while (len-- > 0) - json_object_object_add(proto->header.jso, list[len].name, - _uc_alloc_cfunc(ops, list[len].name, list[len].func)); + ucv_object_add(proto, list[len].name, + ucv_cfunction_new(list[len].name, list[len].func)); - return ops->ressource.define(name, proto, freefn); + return ucv_ressource_type_add(name, proto, freefn); } #define uc_declare_type(name, functions, freefn) \ - _uc_declare_type(ops, name, functions, ARRAY_SIZE(functions), freefn) + _uc_declare_type(name, functions, ARRAY_SIZE(functions), freefn) /* prototype helper */ -static inline bool -uc_add_proto_val(uc_prototype *proto, const char *key, json_object *value) -{ - if (!proto) - return false; - - return json_object_object_add(proto->header.jso, key, value); -} - static inline void -_uc_add_proto_functions(const uc_ops *ops, uc_prototype *proto, const uc_cfunction_list *list, size_t len) +_uc_add_proto_functions(uc_value_t *proto, const uc_cfunction_list *list, size_t len) { while (len-- > 0) - json_object_object_add(proto->header.jso, list[len].name, - _uc_alloc_cfunc(ops, list[len].name, list[len].func)); + ucv_object_add(proto, list[len].name, + ucv_cfunction_new(list[len].name, list[len].func)); } #define uc_add_proto_functions(proto, functions) \ - _uc_add_proto_functions(ops, proto, functions, ARRAY_SIZE(functions)) + _uc_add_proto_functions(proto, functions, ARRAY_SIZE(functions)) #endif /* __LIB_H_ */ diff --git a/lib/fs.c b/lib/fs.c index 7a33529..707e6db 100644 --- a/lib/fs.c +++ b/lib/fs.c @@ -28,29 +28,29 @@ #define err_return(err) do { last_error = err; return NULL; } while(0) //static const uc_ops *ops; -static uc_ressource_type *file_type, *proc_type, *dir_type; +static uc_ressource_type_t *file_type, *proc_type, *dir_type; static int last_error = 0; -static json_object * +static uc_value_t * uc_fs_error(uc_vm *vm, size_t nargs) { - json_object *errmsg; + uc_value_t *errmsg; if (last_error == 0) return NULL; - errmsg = json_object_new_string(strerror(last_error)); + errmsg = ucv_string_new(strerror(last_error)); last_error = 0; return errmsg; } -static json_object * +static uc_value_t * uc_fs_read_common(uc_vm *vm, size_t nargs, const char *type) { - json_object *limit = uc_get_arg(0); - json_object *rv = NULL; + uc_value_t *limit = uc_get_arg(0); + uc_value_t *rv = NULL; char buf[128], *p = NULL, *tmp; size_t rlen, len = 0; const char *lstr; @@ -61,8 +61,8 @@ uc_fs_read_common(uc_vm *vm, size_t nargs, const char *type) if (!fp || !*fp) err_return(EBADF); - if (json_object_is_type(limit, json_type_string)) { - lstr = json_object_get_string(limit); + if (ucv_type(limit) == UC_STRING) { + lstr = ucv_string_get(limit); if (!strcmp(lstr, "line")) { while (true) { @@ -110,8 +110,8 @@ uc_fs_read_common(uc_vm *vm, size_t nargs, const char *type) return NULL; } } - else if (json_object_is_type(limit, json_type_int)) { - lsize = json_object_get_int64(limit); + else if (ucv_type(limit) == UC_INTEGER) { + lsize = ucv_int64_get(limit); if (lsize <= 0) return NULL; @@ -132,43 +132,43 @@ uc_fs_read_common(uc_vm *vm, size_t nargs, const char *type) err_return(EINVAL); } - rv = json_object_new_string_len(p, len); + rv = ucv_string_new_length(p, len); free(p); return rv; } -static json_object * +static uc_value_t * uc_fs_write_common(uc_vm *vm, size_t nargs, const char *type) { - json_object *data = uc_get_arg(0); + uc_value_t *data = uc_get_arg(0); size_t len, wsize; - const char *str; + char *str; FILE **fp = uc_get_self(type); if (!fp || !*fp) err_return(EBADF); - if (json_object_is_type(data, json_type_string)) { - str = json_object_get_string(data); - len = json_object_get_string_len(data); + if (ucv_type(data) == UC_STRING) { + len = ucv_string_length(data); + wsize = fwrite(ucv_string_get(data), 1, len, *fp); } else { - str = json_object_to_json_string(data); + str = ucv_to_jsonstring(vm, data); len = str ? strlen(str) : 0; + wsize = fwrite(str, 1, len, *fp); + free(str); } - wsize = fwrite(str, 1, len, *fp); - if (wsize < len && ferror(*fp)) err_return(errno); - return json_object_new_int64(wsize); + return ucv_int64_new(wsize); } -static json_object * +static uc_value_t * uc_fs_pclose(uc_vm *vm, size_t nargs) { FILE **fp = uc_get_self("fs.proc"); @@ -184,38 +184,38 @@ uc_fs_pclose(uc_vm *vm, size_t nargs) err_return(errno); if (WIFEXITED(rc)) - return xjs_new_int64(WEXITSTATUS(rc)); + return ucv_int64_new(WEXITSTATUS(rc)); if (WIFSIGNALED(rc)) - return xjs_new_int64(-WTERMSIG(rc)); + return ucv_int64_new(-WTERMSIG(rc)); - return xjs_new_int64(0); + return ucv_int64_new(0); } -static json_object * +static uc_value_t * uc_fs_pread(uc_vm *vm, size_t nargs) { return uc_fs_read_common(vm, nargs, "fs.proc"); } -static json_object * +static uc_value_t * uc_fs_pwrite(uc_vm *vm, size_t nargs) { return uc_fs_write_common(vm, nargs, "fs.proc"); } -static json_object * +static uc_value_t * uc_fs_popen(uc_vm *vm, size_t nargs) { - json_object *comm = uc_get_arg(0); - json_object *mode = uc_get_arg(1); + uc_value_t *comm = uc_get_arg(0); + uc_value_t *mode = uc_get_arg(1); FILE *fp; - if (!json_object_is_type(comm, json_type_string)) + if (ucv_type(comm) != UC_STRING) err_return(EINVAL); - fp = popen(json_object_get_string(comm), - json_object_is_type(mode, json_type_string) ? json_object_get_string(mode) : "r"); + fp = popen(ucv_string_get(comm), + ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r"); if (!fp) err_return(errno); @@ -224,7 +224,7 @@ uc_fs_popen(uc_vm *vm, size_t nargs) } -static json_object * +static uc_value_t * uc_fs_close(uc_vm *vm, size_t nargs) { FILE **fp = uc_get_self("fs.file"); @@ -235,26 +235,26 @@ uc_fs_close(uc_vm *vm, size_t nargs) fclose(*fp); *fp = NULL; - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_read(uc_vm *vm, size_t nargs) { return uc_fs_read_common(vm, nargs, "fs.file"); } -static json_object * +static uc_value_t * uc_fs_write(uc_vm *vm, size_t nargs) { return uc_fs_write_common(vm, nargs, "fs.file"); } -static json_object * +static uc_value_t * uc_fs_seek(uc_vm *vm, size_t nargs) { - json_object *ofs = uc_get_arg(0); - json_object *how = uc_get_arg(1); + uc_value_t *ofs = uc_get_arg(0); + uc_value_t *how = uc_get_arg(1); int whence, res; long offset; @@ -265,27 +265,27 @@ uc_fs_seek(uc_vm *vm, size_t nargs) if (!ofs) offset = 0; - else if (!json_object_is_type(ofs, json_type_int)) + else if (ucv_type(ofs) != UC_INTEGER) err_return(EINVAL); else - offset = (long)json_object_get_int64(ofs); + offset = (long)ucv_int64_get(ofs); if (!how) whence = 0; - else if (!json_object_is_type(how, json_type_int)) + else if (ucv_type(how) != UC_INTEGER) err_return(EINVAL); else - whence = (int)json_object_get_int64(how); + whence = (int)ucv_int64_get(how); res = fseek(*fp, offset, whence); if (res < 0) err_return(errno); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_tell(uc_vm *vm, size_t nargs) { long offset; @@ -300,21 +300,21 @@ uc_fs_tell(uc_vm *vm, size_t nargs) if (offset < 0) err_return(errno); - return json_object_new_int64(offset); + return ucv_int64_new(offset); } -static json_object * +static uc_value_t * uc_fs_open(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); - json_object *mode = uc_get_arg(1); + uc_value_t *path = uc_get_arg(0); + uc_value_t *mode = uc_get_arg(1); FILE *fp; - if (!json_object_is_type(path, json_type_string)) + if (ucv_type(path) != UC_STRING) err_return(EINVAL); - fp = fopen(json_object_get_string(path), - json_object_is_type(mode, json_type_string) ? json_object_get_string(mode) : "r"); + fp = fopen(ucv_string_get(path), + ucv_type(mode) == UC_STRING ? ucv_string_get(mode) : "r"); if (!fp) err_return(errno); @@ -323,7 +323,7 @@ uc_fs_open(uc_vm *vm, size_t nargs) } -static json_object * +static uc_value_t * uc_fs_readdir(uc_vm *vm, size_t nargs) { DIR **dp = uc_get_self("fs.dir"); @@ -338,10 +338,10 @@ uc_fs_readdir(uc_vm *vm, size_t nargs) if (!e) err_return(errno); - return json_object_new_string(e->d_name); + return ucv_string_new(e->d_name); } -static json_object * +static uc_value_t * uc_fs_telldir(uc_vm *vm, size_t nargs) { DIR **dp = uc_get_self("fs.dir"); @@ -355,30 +355,30 @@ uc_fs_telldir(uc_vm *vm, size_t nargs) if (position == -1) err_return(errno); - return json_object_new_int64((int64_t)position); + return ucv_int64_new((int64_t)position); } -static json_object * +static uc_value_t * uc_fs_seekdir(uc_vm *vm, size_t nargs) { - json_object *ofs = uc_get_arg(0); + uc_value_t *ofs = uc_get_arg(0); DIR **dp = uc_get_self("fs.dir"); long position; - if (!json_object_is_type(ofs, json_type_int)) + if (ucv_type(ofs) != UC_INTEGER) err_return(EINVAL); if (!dp || !*dp) err_return(EBADF); - position = (long)json_object_get_int64(ofs); + position = (long)ucv_int64_get(ofs); seekdir(*dp, position); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_closedir(uc_vm *vm, size_t nargs) { DIR **dp = uc_get_self("fs.dir"); @@ -389,19 +389,19 @@ uc_fs_closedir(uc_vm *vm, size_t nargs) closedir(*dp); *dp = NULL; - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_opendir(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); + uc_value_t *path = uc_get_arg(0); DIR *dp; - if (!json_object_is_type(path, json_type_string)) + if (ucv_type(path) != UC_STRING) err_return(EINVAL); - dp = opendir(json_object_get_string(path)); + dp = opendir(ucv_string_get(path)); if (!dp) err_return(errno); @@ -409,15 +409,15 @@ uc_fs_opendir(uc_vm *vm, size_t nargs) return uc_alloc_ressource(dir_type, dp); } -static json_object * +static uc_value_t * uc_fs_readlink(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); - json_object *res; + uc_value_t *path = uc_get_arg(0); + uc_value_t *res; ssize_t buflen = 0, rv; char *buf = NULL, *tmp; - if (!json_object_is_type(path, json_type_string)) + if (ucv_type(path) != UC_STRING) err_return(EINVAL); do { @@ -430,7 +430,7 @@ uc_fs_readlink(uc_vm *vm, size_t nargs) } buf = tmp; - rv = readlink(json_object_get_string(path), buf, buflen); + rv = readlink(ucv_string_get(path), buf, buflen); if (rv == -1) { free(buf); @@ -442,173 +442,173 @@ uc_fs_readlink(uc_vm *vm, size_t nargs) } while (true); - res = json_object_new_string_len(buf, rv); + res = ucv_string_new_length(buf, rv); free(buf); return res; } -static json_object * +static uc_value_t * uc_fs_stat_common(uc_vm *vm, size_t nargs, bool use_lstat) { - json_object *path = uc_get_arg(0); - json_object *res, *o; + uc_value_t *path = uc_get_arg(0); + uc_value_t *res, *o; struct stat st; int rv; - if (!json_object_is_type(path, json_type_string)) + if (ucv_type(path) != UC_STRING) err_return(EINVAL); - rv = (use_lstat ? lstat : stat)(json_object_get_string(path), &st); + rv = (use_lstat ? lstat : stat)(ucv_string_get(path), &st); if (rv == -1) err_return(errno); - res = json_object_new_object(); + res = ucv_object_new(vm); if (!res) err_return(ENOMEM); - o = json_object_new_object(); + o = ucv_object_new(vm); if (o) { - json_object_object_add(o, "major", json_object_new_int64(major(st.st_dev))); - json_object_object_add(o, "minor", json_object_new_int64(minor(st.st_dev))); + ucv_object_add(o, "major", ucv_int64_new(major(st.st_dev))); + ucv_object_add(o, "minor", ucv_int64_new(minor(st.st_dev))); - json_object_object_add(res, "dev", o); + ucv_object_add(res, "dev", o); } - o = json_object_new_object(); + o = ucv_object_new(vm); if (o) { - json_object_object_add(o, "setuid", json_object_new_boolean(st.st_mode & S_ISUID)); - json_object_object_add(o, "setgid", json_object_new_boolean(st.st_mode & S_ISGID)); - json_object_object_add(o, "sticky", json_object_new_boolean(st.st_mode & S_ISVTX)); + ucv_object_add(o, "setuid", ucv_boolean_new(st.st_mode & S_ISUID)); + ucv_object_add(o, "setgid", ucv_boolean_new(st.st_mode & S_ISGID)); + ucv_object_add(o, "sticky", ucv_boolean_new(st.st_mode & S_ISVTX)); - json_object_object_add(o, "user_read", json_object_new_boolean(st.st_mode & S_IRUSR)); - json_object_object_add(o, "user_write", json_object_new_boolean(st.st_mode & S_IWUSR)); - json_object_object_add(o, "user_exec", json_object_new_boolean(st.st_mode & S_IXUSR)); + ucv_object_add(o, "user_read", ucv_boolean_new(st.st_mode & S_IRUSR)); + ucv_object_add(o, "user_write", ucv_boolean_new(st.st_mode & S_IWUSR)); + ucv_object_add(o, "user_exec", ucv_boolean_new(st.st_mode & S_IXUSR)); - json_object_object_add(o, "group_read", json_object_new_boolean(st.st_mode & S_IRGRP)); - json_object_object_add(o, "group_write", json_object_new_boolean(st.st_mode & S_IWGRP)); - json_object_object_add(o, "group_exec", json_object_new_boolean(st.st_mode & S_IXGRP)); + ucv_object_add(o, "group_read", ucv_boolean_new(st.st_mode & S_IRGRP)); + ucv_object_add(o, "group_write", ucv_boolean_new(st.st_mode & S_IWGRP)); + ucv_object_add(o, "group_exec", ucv_boolean_new(st.st_mode & S_IXGRP)); - json_object_object_add(o, "other_read", json_object_new_boolean(st.st_mode & S_IROTH)); - json_object_object_add(o, "other_write", json_object_new_boolean(st.st_mode & S_IWOTH)); - json_object_object_add(o, "other_exec", json_object_new_boolean(st.st_mode & S_IXOTH)); + ucv_object_add(o, "other_read", ucv_boolean_new(st.st_mode & S_IROTH)); + ucv_object_add(o, "other_write", ucv_boolean_new(st.st_mode & S_IWOTH)); + ucv_object_add(o, "other_exec", ucv_boolean_new(st.st_mode & S_IXOTH)); - json_object_object_add(res, "perm", o); + ucv_object_add(res, "perm", o); } - json_object_object_add(res, "inode", json_object_new_int64((int64_t)st.st_ino)); - json_object_object_add(res, "mode", json_object_new_int64((int64_t)st.st_mode & ~S_IFMT)); - json_object_object_add(res, "nlink", json_object_new_int64((int64_t)st.st_nlink)); - json_object_object_add(res, "uid", json_object_new_int64((int64_t)st.st_uid)); - json_object_object_add(res, "gid", json_object_new_int64((int64_t)st.st_gid)); - json_object_object_add(res, "size", json_object_new_int64((int64_t)st.st_size)); - json_object_object_add(res, "blksize", json_object_new_int64((int64_t)st.st_blksize)); - json_object_object_add(res, "blocks", json_object_new_int64((int64_t)st.st_blocks)); - json_object_object_add(res, "atime", json_object_new_int64((int64_t)st.st_atime)); - json_object_object_add(res, "mtime", json_object_new_int64((int64_t)st.st_mtime)); - json_object_object_add(res, "ctime", json_object_new_int64((int64_t)st.st_ctime)); + ucv_object_add(res, "inode", ucv_int64_new((int64_t)st.st_ino)); + ucv_object_add(res, "mode", ucv_int64_new((int64_t)st.st_mode & ~S_IFMT)); + ucv_object_add(res, "nlink", ucv_int64_new((int64_t)st.st_nlink)); + ucv_object_add(res, "uid", ucv_int64_new((int64_t)st.st_uid)); + ucv_object_add(res, "gid", ucv_int64_new((int64_t)st.st_gid)); + ucv_object_add(res, "size", ucv_int64_new((int64_t)st.st_size)); + ucv_object_add(res, "blksize", ucv_int64_new((int64_t)st.st_blksize)); + ucv_object_add(res, "blocks", ucv_int64_new((int64_t)st.st_blocks)); + ucv_object_add(res, "atime", ucv_int64_new((int64_t)st.st_atime)); + ucv_object_add(res, "mtime", ucv_int64_new((int64_t)st.st_mtime)); + ucv_object_add(res, "ctime", ucv_int64_new((int64_t)st.st_ctime)); if (S_ISREG(st.st_mode)) - json_object_object_add(res, "type", json_object_new_string("file")); + ucv_object_add(res, "type", ucv_string_new("file")); else if (S_ISDIR(st.st_mode)) - json_object_object_add(res, "type", json_object_new_string("directory")); + ucv_object_add(res, "type", ucv_string_new("directory")); else if (S_ISCHR(st.st_mode)) - json_object_object_add(res, "type", json_object_new_string("char")); + ucv_object_add(res, "type", ucv_string_new("char")); else if (S_ISBLK(st.st_mode)) - json_object_object_add(res, "type", json_object_new_string("block")); + ucv_object_add(res, "type", ucv_string_new("block")); else if (S_ISFIFO(st.st_mode)) - json_object_object_add(res, "type", json_object_new_string("fifo")); + ucv_object_add(res, "type", ucv_string_new("fifo")); else if (S_ISLNK(st.st_mode)) - json_object_object_add(res, "type", json_object_new_string("link")); + ucv_object_add(res, "type", ucv_string_new("link")); else if (S_ISSOCK(st.st_mode)) - json_object_object_add(res, "type", json_object_new_string("socket")); + ucv_object_add(res, "type", ucv_string_new("socket")); else - json_object_object_add(res, "type", json_object_new_string("unknown")); + ucv_object_add(res, "type", ucv_string_new("unknown")); return res; } -static json_object * +static uc_value_t * uc_fs_stat(uc_vm *vm, size_t nargs) { return uc_fs_stat_common(vm, nargs, false); } -static json_object * +static uc_value_t * uc_fs_lstat(uc_vm *vm, size_t nargs) { return uc_fs_stat_common(vm, nargs, true); } -static json_object * +static uc_value_t * uc_fs_mkdir(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); - json_object *mode = uc_get_arg(1); + uc_value_t *path = uc_get_arg(0); + uc_value_t *mode = uc_get_arg(1); - if (!json_object_is_type(path, json_type_string) || - (mode && !json_object_is_type(mode, json_type_int))) + if (ucv_type(path) != UC_STRING || + (mode && ucv_type(mode) != UC_INTEGER)) err_return(EINVAL); - if (mkdir(json_object_get_string(path), (mode_t)(mode ? json_object_get_int64(mode) : 0777)) == -1) + if (mkdir(ucv_string_get(path), (mode_t)(mode ? ucv_int64_get(mode) : 0777)) == -1) err_return(errno); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_rmdir(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); + uc_value_t *path = uc_get_arg(0); - if (!json_object_is_type(path, json_type_string)) + if (ucv_type(path) != UC_STRING) err_return(EINVAL); - if (rmdir(json_object_get_string(path)) == -1) + if (rmdir(ucv_string_get(path)) == -1) err_return(errno); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_symlink(uc_vm *vm, size_t nargs) { - json_object *dest = uc_get_arg(0); - json_object *path = uc_get_arg(1); + uc_value_t *dest = uc_get_arg(0); + uc_value_t *path = uc_get_arg(1); - if (!json_object_is_type(dest, json_type_string) || - !json_object_is_type(path, json_type_string)) + if (ucv_type(dest) != UC_STRING || + ucv_type(path) != UC_STRING) err_return(EINVAL); - if (symlink(json_object_get_string(dest), json_object_get_string(path)) == -1) + if (symlink(ucv_string_get(dest), ucv_string_get(path)) == -1) err_return(errno); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_unlink(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); + uc_value_t *path = uc_get_arg(0); - if (!json_object_is_type(path, json_type_string)) + if (ucv_type(path) != UC_STRING) err_return(EINVAL); - if (unlink(json_object_get_string(path)) == -1) + if (unlink(ucv_string_get(path)) == -1) err_return(errno); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_fs_getcwd(uc_vm *vm, size_t nargs) { - json_object *res; + uc_value_t *res; char *buf = NULL, *tmp; size_t buflen = 0; @@ -634,25 +634,25 @@ uc_fs_getcwd(uc_vm *vm, size_t nargs) } while (true); - res = json_object_new_string(buf); + res = ucv_string_new(buf); free(buf); return res; } -static json_object * +static uc_value_t * uc_fs_chdir(uc_vm *vm, size_t nargs) { - json_object *path = uc_get_arg(0); + uc_value_t *path = uc_get_arg(0); - if (!json_object_is_type(path, json_type_string)) + if (ucv_type(path) != UC_STRING) err_return(EINVAL); - if (chdir(json_object_get_string(path)) == -1) + if (chdir(ucv_string_get(path)) == -1) err_return(errno); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } static const uc_cfunction_list proc_fns[] = { @@ -720,7 +720,7 @@ static void close_dir(void *ud) closedir(dp); } -void uc_module_init(uc_prototype *scope) +void uc_module_init(uc_value_t *scope) { uc_add_proto_functions(scope, global_fns); @@ -728,7 +728,7 @@ void uc_module_init(uc_prototype *scope) file_type = uc_declare_type("fs.file", file_fns, close_file); dir_type = uc_declare_type("fs.dir", dir_fns, close_dir); - uc_add_proto_val(scope, "stdin", uc_alloc_ressource(file_type, stdin)); - uc_add_proto_val(scope, "stdout", uc_alloc_ressource(file_type, stdout)); - uc_add_proto_val(scope, "stderr", uc_alloc_ressource(file_type, stderr)); + ucv_object_add(scope, "stdin", uc_alloc_ressource(file_type, stdin)); + ucv_object_add(scope, "stdout", uc_alloc_ressource(file_type, stdout)); + ucv_object_add(scope, "stderr", uc_alloc_ressource(file_type, stderr)); } diff --git a/lib/math.c b/lib/math.c index 99f3fbc..8f8466e 100644 --- a/lib/math.c +++ b/lib/math.c @@ -21,105 +21,105 @@ static bool srand_called = false; -static json_object * +static uc_value_t * uc_abs(uc_vm *vm, size_t nargs) { - json_object *v = uc_get_arg(0); - enum json_type t; + uc_value_t *v = uc_get_arg(0); + uc_type_t t; int64_t n; double d; - if (json_object_is_type(v, json_type_null)) - return uc_alloc_double(NAN); + if (ucv_type(v) == UC_NULL) + return ucv_double_new(NAN); t = uc_to_number(v, &n, &d); - if (t == json_type_double) - return (isnan(d) || d < 0) ? uc_alloc_double(-d) : json_object_get(v); + if (t == UC_DOUBLE) + return (isnan(d) || d < 0) ? ucv_double_new(-d) : ucv_get(v); - return (n < 0) ? json_object_new_int64(-n) : json_object_get(v); + return (n < 0) ? ucv_int64_new(-n) : ucv_get(v); } -static json_object * +static uc_value_t * uc_atan2(uc_vm *vm, size_t nargs) { double d1 = uc_to_double(uc_get_arg(0)); double d2 = uc_to_double(uc_get_arg(1)); if (isnan(d1) || isnan(d2)) - return uc_alloc_double(NAN); + return ucv_double_new(NAN); - return uc_alloc_double(atan2(d1, d2)); + return ucv_double_new(atan2(d1, d2)); } -static json_object * +static uc_value_t * uc_cos(uc_vm *vm, size_t nargs) { double d = uc_to_double(uc_get_arg(0)); if (isnan(d)) - return uc_alloc_double(NAN); + return ucv_double_new(NAN); - return uc_alloc_double(cos(d)); + return ucv_double_new(cos(d)); } -static json_object * +static uc_value_t * uc_exp(uc_vm *vm, size_t nargs) { double d = uc_to_double(uc_get_arg(0)); if (isnan(d)) - return uc_alloc_double(NAN); + return ucv_double_new(NAN); - return uc_alloc_double(exp(d)); + return ucv_double_new(exp(d)); } -static json_object * +static uc_value_t * uc_log(uc_vm *vm, size_t nargs) { double d = uc_to_double(uc_get_arg(0)); if (isnan(d)) - return uc_alloc_double(NAN); + return ucv_double_new(NAN); - return uc_alloc_double(log(d)); + return ucv_double_new(log(d)); } -static json_object * +static uc_value_t * uc_sin(uc_vm *vm, size_t nargs) { double d = uc_to_double(uc_get_arg(0)); if (isnan(d)) - return uc_alloc_double(NAN); + return ucv_double_new(NAN); - return uc_alloc_double(sin(d)); + return ucv_double_new(sin(d)); } -static json_object * +static uc_value_t * uc_sqrt(uc_vm *vm, size_t nargs) { double d = uc_to_double(uc_get_arg(0)); if (isnan(d)) - return uc_alloc_double(NAN); + return ucv_double_new(NAN); - return uc_alloc_double(sqrt(d)); + return ucv_double_new(sqrt(d)); } -static json_object * +static uc_value_t * uc_pow(uc_vm *vm, size_t nargs) { double x = uc_to_double(uc_get_arg(0)); double y = uc_to_double(uc_get_arg(1)); if (isnan(x) || isnan(y)) - return uc_alloc_double(NAN); + return ucv_double_new(NAN); - return uc_alloc_double(pow(x, y)); + return ucv_double_new(pow(x, y)); } -static json_object * +static uc_value_t * uc_rand(uc_vm *vm, size_t nargs) { struct timeval tv; @@ -131,10 +131,10 @@ uc_rand(uc_vm *vm, size_t nargs) srand_called = true; } - return json_object_new_int64(rand()); + return ucv_int64_new(rand()); } -static json_object * +static uc_value_t * uc_srand(uc_vm *vm, size_t nargs) { int64_t n = uc_to_int64(uc_get_arg(0)); @@ -158,7 +158,7 @@ static const uc_cfunction_list math_fns[] = { { "srand", uc_srand }, }; -void uc_module_init(uc_prototype *scope) +void uc_module_init(uc_value_t *scope) { uc_add_proto_functions(scope, math_fns); } diff --git a/lib/ubus.c b/lib/ubus.c index 47f22d3..4d74710 100644 --- a/lib/ubus.c +++ b/lib/ubus.c @@ -24,7 +24,7 @@ #define err_return(err) do { last_error = err; return NULL; } while(0) static enum ubus_msg_status last_error = 0; -static uc_ressource_type *conn_type; +static uc_ressource_type_t *conn_type; typedef struct { int timeout; @@ -32,28 +32,28 @@ typedef struct { struct ubus_context *ctx; } ubus_connection; -static json_object * +static uc_value_t * uc_ubus_error(uc_vm *vm, size_t nargs) { - json_object *errmsg; + uc_value_t *errmsg; if (last_error == 0) return NULL; - errmsg = json_object_new_string(ubus_strerror(last_error)); + errmsg = ucv_string_new(ubus_strerror(last_error)); last_error = 0; return errmsg; } -static json_object * -uc_blob_to_json(struct blob_attr *attr, bool table, const char **name); +static uc_value_t * +uc_blob_to_json(uc_vm *vm, struct blob_attr *attr, bool table, const char **name); -static json_object * -uc_blob_array_to_json(struct blob_attr *attr, size_t len, bool table) +static uc_value_t * +uc_blob_array_to_json(uc_vm *vm, struct blob_attr *attr, size_t len, bool table) { - json_object *o = table ? json_object_new_object() : json_object_new_array(); - json_object *v; + uc_value_t *o = table ? ucv_object_new(vm) : ucv_array_new(vm); + uc_value_t *v; struct blob_attr *pos; size_t rem = len; const char *name; @@ -63,21 +63,21 @@ uc_blob_array_to_json(struct blob_attr *attr, size_t len, bool table) __blob_for_each_attr(pos, attr, rem) { name = NULL; - v = uc_blob_to_json(pos, table, &name); + v = uc_blob_to_json(vm, pos, table, &name); if (table && name) - json_object_object_add(o, name, v); + ucv_object_add(o, name, v); else if (!table) - json_object_array_add(o, v); + ucv_array_push(o, v); else - json_object_put(v); + ucv_put(v); } return o; } -static json_object * -uc_blob_to_json(struct blob_attr *attr, bool table, const char **name) +static uc_value_t * +uc_blob_to_json(uc_vm *vm, struct blob_attr *attr, bool table, const char **name) { void *data; int len; @@ -93,16 +93,16 @@ uc_blob_to_json(struct blob_attr *attr, bool table, const char **name) switch (blob_id(attr)) { case BLOBMSG_TYPE_BOOL: - return json_object_new_boolean(*(uint8_t *)data); + return ucv_boolean_new(*(uint8_t *)data); case BLOBMSG_TYPE_INT16: - return json_object_new_int64((int64_t)be16_to_cpu(*(uint16_t *)data)); + return ucv_int64_new((int64_t)be16_to_cpu(*(uint16_t *)data)); case BLOBMSG_TYPE_INT32: - return json_object_new_int64((int64_t)be32_to_cpu(*(uint32_t *)data)); + return ucv_int64_new((int64_t)be32_to_cpu(*(uint32_t *)data)); case BLOBMSG_TYPE_INT64: - return json_object_new_uint64(be64_to_cpu(*(uint64_t *)data)); + return ucv_uint64_new(be64_to_cpu(*(uint64_t *)data)); case BLOBMSG_TYPE_DOUBLE: ; @@ -113,16 +113,16 @@ uc_blob_to_json(struct blob_attr *attr, bool table, const char **name) v.u64 = be64_to_cpu(*(uint64_t *)data); - return json_object_new_double(v.d); + return ucv_double_new(v.d); case BLOBMSG_TYPE_STRING: - return json_object_new_string(data); + return ucv_string_new(data); case BLOBMSG_TYPE_ARRAY: - return uc_blob_array_to_json(data, len, false); + return uc_blob_array_to_json(vm, data, len, false); case BLOBMSG_TYPE_TABLE: - return uc_blob_array_to_json(data, len, true); + return uc_blob_array_to_json(vm, data, len, true); default: return NULL; @@ -130,16 +130,16 @@ uc_blob_to_json(struct blob_attr *attr, bool table, const char **name) } -static json_object * +static uc_value_t * uc_ubus_connect(uc_vm *vm, size_t nargs) { - json_object *socket = uc_get_arg(0); - json_object *timeout = uc_get_arg(1); - json_object *co; + uc_value_t *socket = uc_get_arg(0); + uc_value_t *timeout = uc_get_arg(1); + uc_value_t *co; ubus_connection *c; - if ((socket && !json_object_is_type(socket, json_type_string)) || - (timeout && !json_object_is_type(timeout, json_type_int))) + if ((socket && ucv_type(socket) != UC_STRING) || + (timeout && ucv_type(timeout) != UC_INTEGER)) err_return(UBUS_STATUS_INVALID_ARGUMENT); c = calloc(1, sizeof(*c)); @@ -147,8 +147,8 @@ uc_ubus_connect(uc_vm *vm, size_t nargs) if (!c) err_return(UBUS_STATUS_UNKNOWN_ERROR); - c->ctx = ubus_connect(socket ? json_object_get_string(socket) : NULL); - c->timeout = timeout ? json_object_get_int(timeout) : 30; + c->ctx = ubus_connect(socket ? ucv_string_get(socket) : NULL); + c->timeout = timeout ? ucv_int64_get(timeout) : 30; if (!c->ctx) { free(c); @@ -158,7 +158,7 @@ uc_ubus_connect(uc_vm *vm, size_t nargs) if (c->timeout < 0) c->timeout = 30; - co = json_object_new_object(); + co = ucv_object_new(vm); if (!co) { ubus_free(c->ctx); @@ -174,16 +174,16 @@ uc_ubus_connect(uc_vm *vm, size_t nargs) static void uc_ubus_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) { - json_object *arr = p; - json_object *sig; + uc_value_t *arr = p; + uc_value_t *sig; if (!o->signature) return; - sig = uc_blob_array_to_json(blob_data(o->signature), blob_len(o->signature), true); + sig = uc_blob_array_to_json(NULL, blob_data(o->signature), blob_len(o->signature), true); if (sig) - json_object_array_add(arr, sig); + ucv_array_push(arr, sig); } static void @@ -198,27 +198,27 @@ uc_ubus_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) json_object_array_add(arr, obj); } -static json_object * +static uc_value_t * uc_ubus_list(uc_vm *vm, size_t nargs) { ubus_connection **c = uc_get_self("ubus.connection"); - json_object *objname = uc_get_arg(0); - json_object *res = NULL; + uc_value_t *objname = uc_get_arg(0); + uc_value_t *res = NULL; enum ubus_msg_status rv; if (!c || !*c || !(*c)->ctx) err_return(UBUS_STATUS_CONNECTION_FAILED); - if (objname && !json_object_is_type(objname, json_type_string)) + if (objname && ucv_type(objname) != UC_STRING) err_return(UBUS_STATUS_INVALID_ARGUMENT); - res = json_object_new_array(); + res = ucv_array_new(vm); if (!res) err_return(UBUS_STATUS_UNKNOWN_ERROR); rv = ubus_lookup((*c)->ctx, - objname ? json_object_get_string(objname) : NULL, + objname ? ucv_string_get(objname) : NULL, objname ? uc_ubus_signatures_cb : uc_ubus_objects_cb, res); @@ -231,41 +231,48 @@ uc_ubus_list(uc_vm *vm, size_t nargs) static void uc_ubus_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) { - json_object **res = (json_object **)req->priv; + uc_value_t **res = (uc_value_t **)req->priv; - *res = msg ? uc_blob_array_to_json(blob_data(msg), blob_len(msg), true) : NULL; + *res = msg ? uc_blob_array_to_json(NULL, blob_data(msg), blob_len(msg), true) : NULL; } -static json_object * +static uc_value_t * uc_ubus_call(uc_vm *vm, size_t nargs) { ubus_connection **c = uc_get_self("ubus.connection"); - json_object *objname = uc_get_arg(0); - json_object *funname = uc_get_arg(1); - json_object *funargs = uc_get_arg(2); - json_object *res = NULL; + uc_value_t *objname = uc_get_arg(0); + uc_value_t *funname = uc_get_arg(1); + uc_value_t *funargs = uc_get_arg(2); + uc_value_t *res = NULL; + json_object *o; enum ubus_msg_status rv; uint32_t id; if (!c || !*c || !(*c)->ctx) err_return(UBUS_STATUS_CONNECTION_FAILED); - if (!json_object_is_type(objname, json_type_string) || - !json_object_is_type(funname, json_type_string) || - (funargs && !json_object_is_type(funargs, json_type_object))) + if (ucv_type(objname) != UC_STRING || + ucv_type(funname) != UC_STRING || + (funargs && ucv_type(funargs) != UC_OBJECT)) err_return(UBUS_STATUS_INVALID_ARGUMENT); blob_buf_init(&(*c)->buf, 0); - if (funargs && !blobmsg_add_object(&(*c)->buf, funargs)) - err_return(UBUS_STATUS_UNKNOWN_ERROR); + if (funargs) { + o = ucv_to_json(funargs); + rv = blobmsg_add_object(&(*c)->buf, o); + json_object_put(o); + + if (!rv) + err_return(UBUS_STATUS_UNKNOWN_ERROR); + } - rv = ubus_lookup_id((*c)->ctx, json_object_get_string(objname), &id); + rv = ubus_lookup_id((*c)->ctx, ucv_string_get(objname), &id); if (rv != UBUS_STATUS_OK) err_return(rv); - rv = ubus_invoke((*c)->ctx, id, json_object_get_string(funname), (*c)->buf.head, + rv = ubus_invoke((*c)->ctx, id, ucv_string_get(funname), (*c)->buf.head, uc_ubus_call_cb, &res, (*c)->timeout * 1000); if (rv != UBUS_STATUS_OK) @@ -274,7 +281,7 @@ uc_ubus_call(uc_vm *vm, size_t nargs) return res; } -static json_object * +static uc_value_t * uc_ubus_disconnect(uc_vm *vm, size_t nargs) { ubus_connection **c = uc_get_self("ubus.connection"); @@ -285,7 +292,7 @@ uc_ubus_disconnect(uc_vm *vm, size_t nargs) ubus_free((*c)->ctx); (*c)->ctx = NULL; - return json_object_new_boolean(true); + return ucv_boolean_new(true); } @@ -313,7 +320,7 @@ static void close_connection(void *ud) { free(conn); } -void uc_module_init(uc_prototype *scope) +void uc_module_init(uc_value_t *scope) { uc_add_proto_functions(scope, global_fns); diff --git a/lib/uci.c b/lib/uci.c index be98214..fba7dad 100644 --- a/lib/uci.c +++ b/lib/uci.c @@ -22,7 +22,7 @@ #define err_return(err) do { last_error = err; return NULL; } while(0) static int last_error = 0; -static uc_ressource_type *cursor_type; +static uc_ressource_type_t *cursor_type; enum pkg_cmd { CMD_SAVE, @@ -30,11 +30,11 @@ enum pkg_cmd { CMD_REVERT }; -static json_object * +static uc_value_t * uc_uci_error(uc_vm *vm, size_t nargs) { char buf[sizeof("Unknown error: -9223372036854775808")]; - json_object *errmsg; + uc_value_t *errmsg; const char *errstr[] = { [UCI_ERR_MEM] = "Out of memory", @@ -49,12 +49,12 @@ uc_uci_error(uc_vm *vm, size_t nargs) if (last_error == 0) return NULL; - if (last_error >= 0 && last_error < ARRAY_SIZE(errstr)) { - errmsg = json_object_new_string(errstr[last_error]); + if (last_error >= 0 && (unsigned)last_error < ARRAY_SIZE(errstr)) { + errmsg = ucv_string_new(errstr[last_error]); } else { snprintf(buf, sizeof(buf), "Unknown error: %d", last_error); - errmsg = json_object_new_string(buf); + errmsg = ucv_string_new(buf); } last_error = 0; @@ -63,16 +63,16 @@ uc_uci_error(uc_vm *vm, size_t nargs) } -static json_object * +static uc_value_t * uc_uci_cursor(uc_vm *vm, size_t nargs) { - json_object *cdir = uc_get_arg(0); - json_object *sdir = uc_get_arg(1); + uc_value_t *cdir = uc_get_arg(0); + uc_value_t *sdir = uc_get_arg(1); struct uci_context *c; int rv; - if ((cdir && !json_object_is_type(cdir, json_type_string)) || - (sdir && !json_object_is_type(sdir, json_type_string))) + if ((cdir && ucv_type(cdir) != UC_STRING) || + (sdir && ucv_type(sdir) != UC_STRING)) err_return(UCI_ERR_INVAL); c = uci_alloc_context(); @@ -81,14 +81,14 @@ uc_uci_cursor(uc_vm *vm, size_t nargs) err_return(UCI_ERR_MEM); if (cdir) { - rv = uci_set_confdir(c, json_object_get_string(cdir)); + rv = uci_set_confdir(c, ucv_string_get(cdir)); if (rv) err_return(rv); } if (sdir) { - rv = uci_set_savedir(c, json_object_get_string(sdir)); + rv = uci_set_savedir(c, ucv_string_get(sdir)); if (rv) err_return(rv); @@ -98,54 +98,57 @@ uc_uci_cursor(uc_vm *vm, size_t nargs) } -static json_object * +static uc_value_t * uc_uci_load(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); + uc_value_t *conf = uc_get_arg(0); struct uci_element *e; + char *s; if (!c || !*c) err_return(UCI_ERR_INVAL); - if (!json_object_is_type(conf, json_type_string)) + if (ucv_type(conf) != UC_STRING) err_return(UCI_ERR_INVAL); + s = ucv_string_get(conf); + uci_foreach_element(&(*c)->root, e) { - if (!strcmp(e->name, json_object_get_string(conf))) { + if (!strcmp(e->name, s)) { uci_unload(*c, uci_to_package(e)); break; } } - if (uci_load(*c, json_object_get_string(conf), NULL)) + if (uci_load(*c, s, NULL)) err_return((*c)->err); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_uci_unload(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); + uc_value_t *conf = uc_get_arg(0); struct uci_element *e; if (!c || !*c) err_return(UCI_ERR_INVAL); - if (!json_object_is_type(conf, json_type_string)) + if (ucv_type(conf) != UC_STRING) err_return(UCI_ERR_INVAL); uci_foreach_element(&(*c)->root, e) { - if (!strcmp(e->name, json_object_get_string(conf))) { + if (!strcmp(e->name, ucv_string_get(conf))) { uci_unload(*c, uci_to_package(e)); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } } - return json_object_new_boolean(false); + return ucv_boolean_new(false); } static int @@ -177,22 +180,22 @@ lookup_ptr(struct uci_context *ctx, struct uci_ptr *ptr, bool extended) return uci_lookup_ptr(ctx, ptr, NULL, extended); } -static json_object * -option_to_json(struct uci_option *o) +static uc_value_t * +option_to_uval(uc_vm *vm, struct uci_option *o) { - json_object *arr; struct uci_element *e; + uc_value_t *arr; switch (o->type) { case UCI_TYPE_STRING: - return json_object_new_string(o->v.string); + return ucv_string_new(o->v.string); case UCI_TYPE_LIST: - arr = json_object_new_array(); + arr = ucv_array_new(vm); if (arr) uci_foreach_element(&o->v.list, e) - json_object_array_add(arr, json_object_new_string(e->name)); + ucv_array_push(arr, ucv_string_new(e->name)); return arr; @@ -201,36 +204,36 @@ option_to_json(struct uci_option *o) } } -static json_object * -section_to_json(struct uci_section *s, int index) +static uc_value_t * +section_to_uval(uc_vm *vm, struct uci_section *s, int index) { - json_object *so = json_object_new_object(); + uc_value_t *so = ucv_object_new(vm); struct uci_element *e; struct uci_option *o; if (!so) return NULL; - json_object_object_add(so, ".anonymous", json_object_new_boolean(s->anonymous)); - json_object_object_add(so, ".type", json_object_new_string(s->type)); - json_object_object_add(so, ".name", json_object_new_string(s->e.name)); + ucv_object_add(so, ".anonymous", ucv_boolean_new(s->anonymous)); + ucv_object_add(so, ".type", ucv_string_new(s->type)); + ucv_object_add(so, ".name", ucv_string_new(s->e.name)); if (index >= 0) - json_object_object_add(so, ".index", json_object_new_int64(index)); + ucv_object_add(so, ".index", ucv_int64_new(index)); uci_foreach_element(&s->options, e) { o = uci_to_option(e); - json_object_object_add(so, o->e.name, option_to_json(o)); + ucv_object_add(so, o->e.name, option_to_uval(vm, o)); } return so; } -static json_object * -package_to_json(struct uci_package *p) +static uc_value_t * +package_to_uval(uc_vm *vm, struct uci_package *p) { - json_object *po = json_object_new_object(); - json_object *so; + uc_value_t *po = ucv_object_new(vm); + uc_value_t *so; struct uci_element *e; int i = 0; @@ -238,37 +241,37 @@ package_to_json(struct uci_package *p) return NULL; uci_foreach_element(&p->sections, e) { - so = section_to_json(uci_to_section(e), i++); - json_object_object_add(po, e->name, so); + so = section_to_uval(vm, uci_to_section(e), i++); + ucv_object_add(po, e->name, so); } return po; } -static json_object * +static uc_value_t * uc_uci_get_any(uc_vm *vm, size_t nargs, bool all) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *sect = uc_get_arg(1); - json_object *opt = uc_get_arg(2); + uc_value_t *conf = uc_get_arg(0); + uc_value_t *sect = uc_get_arg(1); + uc_value_t *opt = uc_get_arg(2); struct uci_ptr ptr = {}; int rv; if (!c || !*c) err_return(UCI_ERR_INVAL); - if (!json_object_is_type(conf, json_type_string) || - (sect && !json_object_is_type(sect, json_type_string)) || - (opt && !json_object_is_type(opt, json_type_string))) + if ((ucv_type(conf) != UC_STRING) || + (sect && ucv_type(sect) != UC_STRING) || + (opt && ucv_type(opt) != UC_STRING)) err_return(UCI_ERR_INVAL); if ((!sect && !all) || (opt && all)) err_return(UCI_ERR_INVAL); - ptr.package = json_object_get_string(conf); - ptr.section = sect ? json_object_get_string(sect) : NULL; - ptr.option = opt ? json_object_get_string(opt) : NULL; + ptr.package = ucv_string_get(conf); + ptr.section = sect ? ucv_string_get(sect) : NULL; + ptr.option = opt ? ucv_string_get(opt) : NULL; rv = lookup_ptr(*c, &ptr, true); @@ -283,60 +286,60 @@ uc_uci_get_any(uc_vm *vm, size_t nargs, bool all) if (!ptr.s) err_return(UCI_ERR_NOTFOUND); - return section_to_json(ptr.s, -1); + return section_to_uval(vm, ptr.s, -1); } if (!ptr.p) err_return(UCI_ERR_NOTFOUND); - return package_to_json(ptr.p); + return package_to_uval(vm, ptr.p); } if (ptr.option) { if (!ptr.o) err_return(UCI_ERR_NOTFOUND); - return option_to_json(ptr.o); + return option_to_uval(vm, ptr.o); } if (!ptr.s) err_return(UCI_ERR_NOTFOUND); - return json_object_new_string(ptr.s->type); + return ucv_string_new(ptr.s->type); } -static json_object * +static uc_value_t * uc_uci_get(uc_vm *vm, size_t nargs) { return uc_uci_get_any(vm, nargs, false); } -static json_object * +static uc_value_t * uc_uci_get_all(uc_vm *vm, size_t nargs) { return uc_uci_get_any(vm, nargs, true); } -static json_object * +static uc_value_t * uc_uci_get_first(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *type = uc_get_arg(1); - json_object *opt = uc_get_arg(2); + uc_value_t *conf = uc_get_arg(0); + uc_value_t *type = uc_get_arg(1); + uc_value_t *opt = uc_get_arg(2); struct uci_package *p = NULL; struct uci_section *sc; struct uci_element *e; struct uci_ptr ptr = {}; int rv; - if (!json_object_is_type(conf, json_type_string) || - !json_object_is_type(type, json_type_string) || - (opt && !json_object_is_type(opt, json_type_string))) + if (ucv_type(conf) != UC_STRING || + ucv_type(type) != UC_STRING || + (opt && ucv_type(opt) != UC_STRING)) err_return(UCI_ERR_INVAL); uci_foreach_element(&(*c)->root, e) { - if (strcmp(e->name, json_object_get_string(conf))) + if (strcmp(e->name, ucv_string_get(conf))) continue; p = uci_to_package(e); @@ -349,15 +352,15 @@ uc_uci_get_first(uc_vm *vm, size_t nargs) uci_foreach_element(&p->sections, e) { sc = uci_to_section(e); - if (strcmp(sc->type, json_object_get_string(type))) + if (strcmp(sc->type, ucv_string_get(type))) continue; if (!opt) - return json_object_new_string(sc->e.name); + return ucv_string_new(sc->e.name); - ptr.package = json_object_get_string(conf); + ptr.package = ucv_string_get(conf); ptr.section = sc->e.name; - ptr.option = json_object_get_string(opt); + ptr.option = ucv_string_get(opt); ptr.p = p; ptr.s = sc; @@ -369,29 +372,29 @@ uc_uci_get_first(uc_vm *vm, size_t nargs) if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) err_return(UCI_ERR_NOTFOUND); - return option_to_json(ptr.o); + return option_to_uval(vm, ptr.o); } err_return(UCI_ERR_NOTFOUND); } -static json_object * +static uc_value_t * uc_uci_add(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *type = uc_get_arg(1); + uc_value_t *conf = uc_get_arg(0); + uc_value_t *type = uc_get_arg(1); struct uci_element *e = NULL; struct uci_package *p = NULL; struct uci_section *sc = NULL; int rv; - if (!json_object_is_type(conf, json_type_string) || - !json_object_is_type(type, json_type_string)) + if (ucv_type(conf) != UC_STRING || + ucv_type(type) != UC_STRING) err_return(UCI_ERR_INVAL); uci_foreach_element(&(*c)->root, e) { - if (!strcmp(e->name, json_object_get_string(conf))) { + if (!strcmp(e->name, ucv_string_get(conf))) { p = uci_to_package(e); break; } @@ -400,73 +403,75 @@ uc_uci_add(uc_vm *vm, size_t nargs) if (!p) err_return(UCI_ERR_NOTFOUND); - rv = uci_add_section(*c, p, json_object_get_string(type), &sc); + rv = uci_add_section(*c, p, ucv_string_get(type), &sc); if (rv != UCI_OK) err_return(rv); else if (!sc) err_return(UCI_ERR_NOTFOUND); - return json_object_new_string(sc->e.name); + return ucv_string_new(sc->e.name); } static bool -json_to_value(json_object *val, const char **p, bool *is_list) +uval_to_uci(uc_vm *vm, uc_value_t *val, const char **p, bool *is_list) { - json_object *item; + uc_value_t *item; *p = NULL; if (is_list) *is_list = false; - switch (json_object_get_type(val)) { - case json_type_object: - return false; - - case json_type_array: - if (json_object_array_length(val) == 0) + switch (ucv_type(val)) { + case UC_ARRAY: + if (ucv_array_length(val) == 0) return false; - item = json_object_array_get_idx(val, 0); + item = ucv_array_get(val, 0); /* don't recurse */ - if (json_object_is_type(item, json_type_array)) + if (ucv_type(item) == UC_ARRAY) return false; if (is_list) *is_list = true; - return json_to_value(item, p, NULL); + return uval_to_uci(vm, item, p, NULL); - case json_type_boolean: - *p = json_object_get_boolean(val) ? "1" : "0"; + case UC_BOOLEAN: + *p = xstrdup(ucv_boolean_get(val) ? "1" : "0"); return true; - case json_type_null: + case UC_DOUBLE: + case UC_INTEGER: + case UC_STRING: + *p = ucv_to_string(vm, val); + /* fall through */ + + case UC_NULL: return true; default: - *p = json_object_get_string(val); - - return true; + return false; } } -static json_object * +static uc_value_t * uc_uci_set(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *sect = uc_get_arg(1); - json_object *opt = NULL, *val = NULL; + uc_value_t *conf = uc_get_arg(0); + uc_value_t *sect = uc_get_arg(1); + uc_value_t *opt = NULL, *val = NULL; struct uci_ptr ptr = {}; bool is_list = false; - int rv, i; + size_t i; + int rv; - if (!json_object_is_type(conf, json_type_string) || - !json_object_is_type(sect, json_type_string)) + if (ucv_type(conf) != UC_STRING || + ucv_type(sect) != UC_STRING) err_return(UCI_ERR_INVAL); switch (nargs) { @@ -475,7 +480,7 @@ uc_uci_set(uc_vm *vm, size_t nargs) opt = uc_get_arg(2); val = uc_get_arg(3); - if (!json_object_is_type(opt, json_type_string)) + if (ucv_type(opt) != UC_STRING) err_return(UCI_ERR_INVAL); break; @@ -484,7 +489,7 @@ uc_uci_set(uc_vm *vm, size_t nargs) case 3: val = uc_get_arg(2); - if (!json_object_is_type(val, json_type_string)) + if (ucv_type(val) != UC_STRING) err_return(UCI_ERR_INVAL); break; @@ -493,9 +498,9 @@ uc_uci_set(uc_vm *vm, size_t nargs) err_return(UCI_ERR_INVAL); } - ptr.package = json_object_get_string(conf); - ptr.section = json_object_get_string(sect); - ptr.option = opt ? json_object_get_string(opt) : NULL; + ptr.package = ucv_string_get(conf); + ptr.section = ucv_string_get(sect); + ptr.option = opt ? ucv_string_get(opt) : NULL; rv = lookup_ptr(*c, &ptr, true); @@ -505,18 +510,19 @@ uc_uci_set(uc_vm *vm, size_t nargs) if (!ptr.s && ptr.option) err_return(UCI_ERR_NOTFOUND); - if (!json_to_value(val, &ptr.value, &is_list)) + if (!uval_to_uci(vm, val, &ptr.value, &is_list)) err_return(UCI_ERR_INVAL); if (is_list) { /* if we got a one-element array, delete existing option (if any) * and iterate array at offset 0 */ - if (json_object_array_length(val) == 1) { + if (ucv_array_length(val) == 1) { i = 0; - if (ptr.o) { - ptr.value = NULL; + free((char *)ptr.value); + ptr.value = NULL; + if (ptr.o) { rv = uci_delete(*c, &ptr); if (rv != UCI_OK) @@ -529,16 +535,18 @@ uc_uci_set(uc_vm *vm, size_t nargs) i = 1; rv = uci_set(*c, &ptr); + free((char *)ptr.value); if (rv != UCI_OK) err_return(rv); } - for (; i < json_object_array_length(val); i++) { - if (!json_to_value(json_object_array_get_idx(val, i), &ptr.value, NULL)) + for (; i < ucv_array_length(val); i++) { + if (!uval_to_uci(vm, ucv_array_get(val, i), &ptr.value, NULL)) continue; rv = uci_add_list(*c, &ptr); + free((char *)ptr.value); if (rv != UCI_OK) err_return(rv); @@ -546,32 +554,33 @@ uc_uci_set(uc_vm *vm, size_t nargs) } else { rv = uci_set(*c, &ptr); + free((char *)ptr.value); if (rv != UCI_OK) err_return(rv); } - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_uci_delete(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *sect = uc_get_arg(1); - json_object *opt = uc_get_arg(2); + uc_value_t *conf = uc_get_arg(0); + uc_value_t *sect = uc_get_arg(1); + uc_value_t *opt = uc_get_arg(2); struct uci_ptr ptr = {}; int rv; - if (!json_object_is_type(conf, json_type_string) || - !json_object_is_type(sect, json_type_string) || - (opt && !json_object_is_type(opt, json_type_string))) + if (ucv_type(conf) != UC_STRING || + ucv_type(sect) != UC_STRING || + (opt && ucv_type(opt) != UC_STRING)) err_return(UCI_ERR_INVAL); - ptr.package = json_object_get_string(conf); - ptr.section = json_object_get_string(sect); - ptr.option = opt ? json_object_get_string(opt) : NULL; + ptr.package = ucv_string_get(conf); + ptr.section = ucv_string_get(sect); + ptr.option = opt ? ucv_string_get(opt) : NULL; rv = lookup_ptr(*c, &ptr, true); @@ -586,21 +595,21 @@ uc_uci_delete(uc_vm *vm, size_t nargs) if (rv != UCI_OK) err_return(rv); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_uci_rename(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *sect = uc_get_arg(1); - json_object *opt = NULL, *val = NULL; + uc_value_t *conf = uc_get_arg(0); + uc_value_t *sect = uc_get_arg(1); + uc_value_t *opt = NULL, *val = NULL; struct uci_ptr ptr = {}; int rv; - if (!json_object_is_type(conf, json_type_string) || - !json_object_is_type(sect, json_type_string)) + if (ucv_type(conf) != UC_STRING || + ucv_type(sect) != UC_STRING) err_return(UCI_ERR_INVAL); switch (nargs) { @@ -609,8 +618,8 @@ uc_uci_rename(uc_vm *vm, size_t nargs) opt = uc_get_arg(2); val = uc_get_arg(3); - if (!json_object_is_type(opt, json_type_string) || - !json_object_is_type(val, json_type_string)) + if (ucv_type(opt) != UC_STRING || + ucv_type(val) != UC_STRING) err_return(UCI_ERR_INVAL); break; @@ -619,7 +628,7 @@ uc_uci_rename(uc_vm *vm, size_t nargs) case 3: val = uc_get_arg(2); - if (!json_object_is_type(val, json_type_string)) + if (ucv_type(val) != UC_STRING) err_return(UCI_ERR_INVAL); break; @@ -628,10 +637,10 @@ uc_uci_rename(uc_vm *vm, size_t nargs) err_return(UCI_ERR_INVAL); } - ptr.package = json_object_get_string(conf); - ptr.section = json_object_get_string(sect); - ptr.option = opt ? json_object_get_string(opt) : NULL; - ptr.value = json_object_get_string(val); + ptr.package = ucv_string_get(conf); + ptr.section = ucv_string_get(sect); + ptr.option = opt ? ucv_string_get(opt) : NULL; + ptr.value = ucv_string_get(val); rv = lookup_ptr(*c, &ptr, true); @@ -646,32 +655,32 @@ uc_uci_rename(uc_vm *vm, size_t nargs) if (rv != UCI_OK) err_return(rv); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_uci_reorder(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *sect = uc_get_arg(1); - json_object *val = uc_get_arg(2); + uc_value_t *conf = uc_get_arg(0); + uc_value_t *sect = uc_get_arg(1); + uc_value_t *val = uc_get_arg(2); struct uci_ptr ptr = {}; int64_t n; int rv; - if (!json_object_is_type(conf, json_type_string) || - !json_object_is_type(sect, json_type_string) || - !json_object_is_type(val, json_type_int)) + if (ucv_type(conf) != UC_STRING || + ucv_type(sect) != UC_STRING || + ucv_type(val) != UC_INTEGER) err_return(UCI_ERR_INVAL); - n = json_object_get_int64(val); + n = ucv_int64_get(val); if (n < 0) err_return(UCI_ERR_INVAL); - ptr.package = json_object_get_string(conf); - ptr.section = json_object_get_string(sect); + ptr.package = ucv_string_get(conf); + ptr.section = ucv_string_get(sect); rv = lookup_ptr(*c, &ptr, true); @@ -686,14 +695,14 @@ uc_uci_reorder(uc_vm *vm, size_t nargs) if (rv != UCI_OK) err_return(rv); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_uci_pkg_command(uc_vm *vm, size_t nargs, enum pkg_cmd cmd) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); + uc_value_t *conf = uc_get_arg(0); struct uci_element *e, *tmp; struct uci_package *p; struct uci_ptr ptr = {}; @@ -702,13 +711,13 @@ uc_uci_pkg_command(uc_vm *vm, size_t nargs, enum pkg_cmd cmd) if (cmd != CMD_REVERT && conf) err_return(UCI_ERR_INVAL); - if (conf && !json_object_is_type(conf, json_type_string)) + if (conf && ucv_type(conf) != UC_STRING) err_return(UCI_ERR_INVAL); uci_foreach_element_safe(&(*c)->root, tmp, e) { p = uci_to_package(e); - if (conf && strcmp(e->name, json_object_get_string(conf))) + if (conf && strcmp(e->name, ucv_string_get(conf))) continue; switch (cmd) { @@ -736,29 +745,29 @@ uc_uci_pkg_command(uc_vm *vm, size_t nargs, enum pkg_cmd cmd) if (res != UCI_OK) err_return(res); - return json_object_new_boolean(true); + return ucv_boolean_new(true); } -static json_object * +static uc_value_t * uc_uci_save(uc_vm *vm, size_t nargs) { return uc_uci_pkg_command(vm, nargs, CMD_SAVE); } -static json_object * +static uc_value_t * uc_uci_commit(uc_vm *vm, size_t nargs) { return uc_uci_pkg_command(vm, nargs, CMD_COMMIT); } -static json_object * +static uc_value_t * uc_uci_revert(uc_vm *vm, size_t nargs) { return uc_uci_pkg_command(vm, nargs, CMD_REVERT); } -static json_object * -change_to_json(struct uci_delta *d) +static uc_value_t * +change_to_uval(uc_vm *vm, struct uci_delta *d) { const char *types[] = { [UCI_CMD_REORDER] = "order", @@ -770,36 +779,36 @@ change_to_json(struct uci_delta *d) [UCI_CMD_CHANGE] = "set", }; - json_object *a; + uc_value_t *a; if (!d->section) return NULL; - a = json_object_new_array(); + a = ucv_array_new(vm); if (!a) return NULL; - json_object_array_add(a, json_object_new_string(types[d->cmd])); - json_object_array_add(a, json_object_new_string(d->section)); + ucv_array_push(a, ucv_string_new(types[d->cmd])); + ucv_array_push(a, ucv_string_new(d->section)); if (d->e.name) - json_object_array_add(a, json_object_new_string(d->e.name)); + ucv_array_push(a, ucv_string_new(d->e.name)); if (d->value) { if (d->cmd == UCI_CMD_REORDER) - json_object_array_add(a, json_object_new_int64(strtoul(d->value, NULL, 10))); + ucv_array_push(a, ucv_int64_new(strtoul(d->value, NULL, 10))); else - json_object_array_add(a, json_object_new_string(d->value)); + ucv_array_push(a, ucv_string_new(d->value)); } return a; } -static json_object * -changes_to_json(struct uci_context *ctx, const char *package) +static uc_value_t * +changes_to_uval(uc_vm *vm, struct uci_context *ctx, const char *package) { - json_object *a = NULL, *c; + uc_value_t *a = NULL, *c; struct uci_package *p = NULL; struct uci_element *e; bool unload = false; @@ -820,23 +829,23 @@ changes_to_json(struct uci_context *ctx, const char *package) return NULL; if (!uci_list_empty(&p->delta) || !uci_list_empty(&p->saved_delta)) { - a = json_object_new_array(); + a = ucv_array_new(vm); if (!a) err_return(UCI_ERR_MEM); uci_foreach_element(&p->saved_delta, e) { - c = change_to_json(uci_to_delta(e)); + c = change_to_uval(vm, uci_to_delta(e)); if (c) - json_object_array_add(a, c); + ucv_array_push(a, c); } uci_foreach_element(&p->delta, e) { - c = change_to_json(uci_to_delta(e)); + c = change_to_uval(vm, uci_to_delta(e)); if (c) - json_object_array_add(a, c); + ucv_array_push(a, c); } } @@ -846,16 +855,16 @@ changes_to_json(struct uci_context *ctx, const char *package) return a; } -static json_object * +static uc_value_t * uc_uci_changes(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *res, *chg; + uc_value_t *conf = uc_get_arg(0); + uc_value_t *res, *chg; char **configs; int rv, i; - if (conf && !json_object_is_type(conf, json_type_string)) + if (conf && ucv_type(conf) != UC_STRING) err_return(UCI_ERR_INVAL); rv = uci_list_configs(*c, &configs); @@ -863,21 +872,16 @@ uc_uci_changes(uc_vm *vm, size_t nargs) if (rv != UCI_OK) err_return(rv); - res = json_object_new_object(); - - if (!res) { - free(configs); - err_return(UCI_ERR_MEM); - } + res = ucv_object_new(vm); for (i = 0; configs[i]; i++) { - if (conf && strcmp(configs[i], json_object_get_string(conf))) + if (conf && strcmp(configs[i], ucv_string_get(conf))) continue; - chg = changes_to_json(*c, configs[i]); + chg = changes_to_uval(vm, *c, configs[i]); if (chg) - json_object_object_add(res, configs[i], chg); + ucv_object_add(res, configs[i], chg); } free(configs); @@ -885,14 +889,14 @@ uc_uci_changes(uc_vm *vm, size_t nargs) return res; } -static json_object * +static uc_value_t * uc_uci_foreach(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *conf = uc_get_arg(0); - json_object *type = uc_get_arg(1); - json_object *func = uc_get_arg(2); - json_object *rv = NULL; + uc_value_t *conf = uc_get_arg(0); + uc_value_t *type = uc_get_arg(1); + uc_value_t *func = uc_get_arg(2); + uc_value_t *rv = NULL; struct uci_package *p = NULL; struct uci_element *e, *tmp; struct uci_section *sc; @@ -901,12 +905,12 @@ uc_uci_foreach(uc_vm *vm, size_t nargs) bool ret = false; int i = 0; - if (!json_object_is_type(conf, json_type_string) || - (type && !json_object_is_type(type, json_type_string))) + if (ucv_type(conf) != UC_STRING || + (type && ucv_type(type) != UC_STRING)) err_return(UCI_ERR_INVAL); uci_foreach_element(&(*c)->root, e) { - if (strcmp(e->name, json_object_get_string(conf))) + if (strcmp(e->name, ucv_string_get(conf))) continue; p = uci_to_package(e); @@ -920,11 +924,11 @@ uc_uci_foreach(uc_vm *vm, size_t nargs) sc = uci_to_section(e); i++; - if (type && strcmp(sc->type, json_object_get_string(type))) + if (type && strcmp(sc->type, ucv_string_get(type))) continue; - uc_push_val(uc_value_get(func)); - uc_push_val(section_to_json(sc, i - 1)); + uc_push_val(ucv_get(func)); + uc_push_val(section_to_uval(vm, sc, i - 1)); ex = uc_call(1); @@ -934,9 +938,9 @@ uc_uci_foreach(uc_vm *vm, size_t nargs) ret = true; rv = uc_pop_val(); - stop = (json_object_is_type(rv, json_type_boolean) && !json_object_get_boolean(rv)); + stop = (ucv_type(rv) == UC_BOOLEAN && !ucv_boolean_get(rv)); - json_object_put(rv); + ucv_put(rv); if (stop) break; @@ -944,14 +948,14 @@ uc_uci_foreach(uc_vm *vm, size_t nargs) /* XXX: rethrow */ - return json_object_new_boolean(ret); + return ucv_boolean_new(ret); } -static json_object * +static uc_value_t * uc_uci_configs(uc_vm *vm, size_t nargs) { struct uci_context **c = uc_get_self("uci.cursor"); - json_object *a; + uc_value_t *a; char **configs; int i, rv; @@ -960,15 +964,10 @@ uc_uci_configs(uc_vm *vm, size_t nargs) if (rv != UCI_OK) err_return(rv); - a = json_object_new_array(); - - if (!a) { - free(configs); - err_return(UCI_ERR_MEM); - } + a = ucv_array_new(vm); for (i = 0; configs[i]; i++) - json_object_array_add(a, json_object_new_string(configs[i])); + ucv_array_push(a, ucv_string_new(configs[i])); free(configs); @@ -1006,7 +1005,7 @@ static void close_uci(void *ud) { uci_free_context((struct uci_context *)ud); } -void uc_module_init(uc_prototype *scope) +void uc_module_init(uc_value_t *scope) { uc_add_proto_functions(scope, global_fns); diff --git a/main.c b/main.c index c185524..e762312 100644 --- a/main.c +++ b/main.c @@ -55,14 +55,14 @@ print_usage(const char *app) } static void -globals_init(uc_prototype *scope) +globals_init(uc_vm *vm, uc_value_t *scope) { - json_object *arr = xjs_new_array(); + uc_value_t *arr = ucv_array_new(vm); const char *p, *last; for (p = last = LIB_SEARCH_PATH;; p++) { if (*p == ':' || *p == '\0') { - json_object_array_add(arr, xjs_new_string_len(last, p - last)); + ucv_array_push(arr, ucv_string_new_length(last, p - last)); if (!*p) break; @@ -71,11 +71,11 @@ globals_init(uc_prototype *scope) } } - json_object_object_add(scope->header.jso, "REQUIRE_SEARCH_PATH", arr); + ucv_object_add(scope, "REQUIRE_SEARCH_PATH", arr); } static void -register_variable(uc_prototype *scope, const char *key, json_object *val) +register_variable(uc_value_t *scope, const char *key, uc_value_t *val) { char *name = strdup(key); char *p; @@ -87,17 +87,17 @@ register_variable(uc_prototype *scope, const char *key, json_object *val) if (!isalnum(*p) && *p != '_') *p = '_'; - json_object_object_add(scope->header.jso, name, val); + ucv_object_add(scope, name, val); free(name); } static int parse(uc_parse_config *config, uc_source *src, - bool skip_shebang, json_object *env, json_object *modules) + bool skip_shebang, uc_value_t *env, uc_value_t *modules) { - uc_prototype *globals = uc_prototype_new(NULL), *rootscope = NULL; - uc_function *entry; + uc_value_t *globals = NULL; + uc_function_t *entry; uc_vm vm = {}; char c, c2, *err; int rc = 0; @@ -131,25 +131,24 @@ parse(uc_parse_config *config, uc_source *src, goto out; } + globals = ucv_object_new(&vm); + /* load global variables */ - globals_init(globals); + globals_init(&vm, globals); /* load env variables */ if (env) { - json_object_object_foreach(env, key, val) - register_variable(globals, key, uc_value_get(val)); + ucv_object_foreach(env, key, val) + register_variable(globals, key, ucv_get(val)); } /* load std functions into global scope */ uc_lib_init(globals); /* create instance of global scope, set "global" property on it */ - rootscope = uc_protoref_new(xjs_new_object(), globals); - - json_object_object_add(rootscope->header.jso, "global", - uc_value_get(globals->header.jso)); + ucv_object_add(globals, "global", ucv_get(globals)); - rc = uc_vm_execute(&vm, entry, rootscope, modules); + rc = uc_vm_execute(&vm, entry, globals, modules); if (rc) { rc = 1; @@ -158,10 +157,6 @@ parse(uc_parse_config *config, uc_source *src, out: uc_vm_free(&vm); - uc_value_put(globals->header.jso); - - if (rootscope) - uc_value_put(rootscope->header.jso); return rc; } @@ -193,12 +188,13 @@ read_stdin(char **ptr) return uc_source_new_buffer("[stdin]", *ptr, tlen); } -static json_object * +static uc_value_t * parse_envfile(FILE *fp) { - json_object *rv = NULL; enum json_tokener_error err = json_tokener_continue; struct json_tokener *tok; + json_object *jso = NULL; + uc_value_t *rv; char buf[128]; size_t rlen; @@ -210,27 +206,32 @@ parse_envfile(FILE *fp) if (rlen == 0) break; - rv = json_tokener_parse_ex(tok, buf, rlen); + jso = json_tokener_parse_ex(tok, buf, rlen); err = json_tokener_get_error(tok); if (err != json_tokener_continue) break; } - if (err != json_tokener_success || !json_object_is_type(rv, json_type_object)) { - json_object_put(rv); - rv = NULL; + if (err != json_tokener_success || !json_object_is_type(jso, json_type_object)) { + json_object_put(jso); + + return NULL; } json_tokener_free(tok); + rv = ucv_from_json(NULL, jso); + + json_object_put(jso); + return rv; } int main(int argc, char **argv) { - json_object *env = NULL, *modules = NULL, *o, *p; + uc_value_t *env = NULL, *modules = NULL, *o, *p; uc_source *source = NULL, *envfile = NULL; char *stdin = NULL, *c; bool shebang = false; @@ -335,27 +336,27 @@ main(int argc, char **argv) goto out; } - env = env ? env : xjs_new_object(); + env = env ? env : ucv_object_new(NULL); if (c > optarg && optarg[0]) { - p = xjs_new_object(); - json_object_object_add(env, optarg, p); + p = ucv_object_new(NULL); + ucv_object_add(env, optarg, p); } else { p = env; } - json_object_object_foreach(o, key, val) - json_object_object_add(p, key, json_object_get(val)); + ucv_object_foreach(o, key, val) + ucv_object_add(p, key, ucv_get(val)); - json_object_put(o); + ucv_put(o); break; case 'm': - modules = modules ? modules : xjs_new_array(); + modules = modules ? modules : ucv_array_new(NULL); - json_object_array_add(modules, xjs_new_string(optarg)); + ucv_array_push(modules, ucv_string_new(optarg)); break; } @@ -382,8 +383,8 @@ main(int argc, char **argv) rv = parse(&config, source, shebang, env, modules); out: - json_object_put(modules); - json_object_put(env); + ucv_put(modules); + ucv_put(env); uc_source_put(source); diff --git a/module.h b/module.h index 9885270..aa72074 100644 --- a/module.h +++ b/module.h @@ -18,7 +18,6 @@ #define __MODULE_H_ #include "lib.h" -#include "object.h" #include "vm.h" #define register_functions(scope, functions) \ @@ -34,23 +33,19 @@ }) #define declare_type(name, proto, freefn) \ - ops->ressource.define(name, proto, freefn) + ucv_ressource_type_add(name, proto, freefn) #define alloc_ressource(data, type) \ - ops->ressource.create(xjs_new_object(), type, data) + ucv_ressource_new(ucv_object_new(NULL), type, data) #define register_ressource(scope, key, res) \ json_object_object_add((scope)->header.jso, key, (res)->header.jso) -static const uc_ops *ops; +void uc_module_init(uc_value_t *scope) __attribute__((weak)); -void uc_module_init(uc_prototype *scope) __attribute__((weak)); - -void uc_module_entry(const uc_ops *_ops, uc_prototype *scope); -void uc_module_entry(const uc_ops *_ops, uc_prototype *scope) +void uc_module_entry(uc_value_t *scope); +void uc_module_entry(uc_value_t *scope) { - ops = _ops; - if (uc_module_init) uc_module_init(scope); } diff --git a/object.c b/object.c deleted file mode 100644 index 7748744..0000000 --- a/object.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (C) 2020-2021 Jo-Philipp Wich - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include - -#include "object.h" - -static void * -uc_object_new(uc_objtype_t type, size_t size, json_object_to_json_string_fn *tostring, json_object_delete_fn *gc) -{ - uc_objhdr *hdr = xalloc(size); - - hdr->type = type; - hdr->jso = xjs_new_object(); - - json_object_set_serializer(hdr->jso, tostring, hdr, gc); - - return hdr; -} - -static int -uc_upvalref_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - return sprintbuf(pb, "", jso); -} - -static void -uc_upvalref_gc(json_object *jso, void *userdata) -{ - uc_upvalref *up = userdata; - - uc_value_put(up->value); - free(up); -} - -uc_upvalref * -uc_upvalref_new(size_t slot) -{ - uc_upvalref *up; - - up = uc_object_new(UC_OBJ_UPVAL, sizeof(*up), uc_upvalref_tostring, uc_upvalref_gc); - up->slot = slot; - - return up; -} - -static int -uc_function_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - return sprintbuf(pb, "", jso); -} - -static void -uc_function_gc(json_object *jso, void *userdata) -{ - uc_function *fn = userdata; - - uc_chunk_free(&fn->chunk); - uc_source_put(fn->source); - - free(fn); -} - -uc_function * -uc_function_new(const char *name, size_t srcpos, uc_source *source) -{ - size_t namelen = 0; - uc_function *fn; - - if (name) - namelen = strlen(name) + 1; - - fn = uc_object_new(UC_OBJ_FUNCTION, ALIGN(sizeof(*fn)) + namelen, uc_function_tostring, uc_function_gc); - fn->name = name ? strcpy((char *)fn + ALIGN(sizeof(*fn)), name) : NULL; - fn->nargs = 0; - fn->nupvals = 0; - fn->srcpos = srcpos; - fn->source = uc_source_get(source); - fn->vararg = false; - - uc_chunk_init(&fn->chunk); - - return fn; -} - -size_t -uc_function_get_srcpos(uc_function *fn, size_t off) -{ - size_t pos = uc_chunk_debug_get_srcpos(&fn->chunk, off); - - return pos ? fn->srcpos + pos : 0; -} - -static int -uc_closure_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_closure *closure = json_object_get_userdata(jso); - uc_function *function = closure->function; - json_object *argname; - size_t i; - - sprintbuf(pb, "%s%s", - strict ? "\"" : "", - closure->is_arrow ? "" : "function"); - - if (function->name) - sprintbuf(pb, " %s", function->name); - - sprintbuf(pb, "("); - - for (i = 1; i <= function->nargs; i++) { - argname = uc_chunk_debug_get_variable(&function->chunk, i - 1, i, false); - - if (i > 1) - sprintbuf(pb, ", "); - - if (i == function->nargs && function->vararg) - sprintbuf(pb, "..."); - - if (argname) - sprintbuf(pb, "%s", json_object_get_string(argname)); - else - sprintbuf(pb, "[arg%zu]", i); - - uc_value_put(argname); - } - - return sprintbuf(pb, ")%s { ... }%s", - closure->is_arrow ? " =>" : "", - strict ? "\"" : ""); -} - -static void -uc_closure_gc(json_object *jso, void *userdata) -{ - uc_closure *closure = userdata; - uc_function *function = closure->function; - size_t i; - - for (i = 0; i < function->nupvals; i++) - uc_value_put(closure->upvals[i]->header.jso); - - uc_value_put(function->header.jso); - - free(closure); -} - -uc_closure * -uc_closure_new(uc_function *function, bool arrow_fn) -{ - uc_closure *closure; - - closure = uc_object_new(UC_OBJ_CLOSURE, - ALIGN(sizeof(*closure)) + (sizeof(uc_upvalref *) * function->nupvals), - uc_closure_tostring, uc_closure_gc); - - closure->function = function; - closure->is_arrow = arrow_fn; - closure->upvals = function->nupvals ? ((void *)closure + ALIGN(sizeof(*closure))) : NULL; - - return closure; -} - -static int -uc_cfunction_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_cfunction *cfn = json_object_get_userdata(jso); - - return sprintbuf(pb, "%sfunction%s%s(...) { [native code] }%s", - strict ? "\"" : "", - cfn->name ? " " : "", - cfn->name ? cfn->name : "", - strict ? "\"" : ""); -} - -static void -uc_cfunction_gc(json_object *jso, void *userdata) -{ - free(userdata); -} - -uc_cfunction * -uc_cfunction_new(const char *name, uc_cfn_ptr fptr) -{ - size_t namelen = 0; - uc_cfunction *cfn; - - if (name) - namelen = strlen(name) + 1; - - cfn = uc_object_new(UC_OBJ_CFUNCTION, ALIGN(sizeof(*cfn)) + namelen, uc_cfunction_tostring, uc_cfunction_gc); - cfn->name = name ? strcpy((char *)cfn + ALIGN(sizeof(*cfn)), name) : NULL; - cfn->cfn = fptr; - - return cfn; -} - -static int -uc_regexp_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_regexp *re = json_object_get_userdata(jso); - json_object *s; - const char *p; - size_t len; - - sprintbuf(pb, "%s/", strict ? "\"" : ""); - - s = xjs_new_string(re->pattern); - - if (strict) - for (p = json_object_to_json_string(s) + 1, len = strlen(p) - 1; len > 0; len--, p++) - sprintbuf(pb, "%c", *p); - else - sprintbuf(pb, "%s", json_object_get_string(s)); - - uc_value_put(s); - - return sprintbuf(pb, "/%s%s%s%s", - re->global ? "g" : "", - re->icase ? "i" : "", - re->newline ? "s" : "", - strict ? "\"" : ""); -} - -static void -uc_regexp_gc(json_object *jso, void *userdata) -{ - uc_regexp *re = userdata; - - regfree(&re->re); - free(re); -} - -uc_regexp * -uc_regexp_new(const char *pattern, bool icase, bool newline, bool global, char **err) -{ - int cflags = REG_EXTENDED, res; - uc_regexp *re; - size_t len; - - re = uc_object_new(UC_OBJ_REGEXP, ALIGN(sizeof(*re)) + strlen(pattern) + 1, uc_regexp_tostring, uc_regexp_gc); - re->icase = icase; - re->global = global; - re->newline = newline; - re->pattern = strcpy((char *)re + ALIGN(sizeof(*re)), pattern); - - if (icase) - cflags |= REG_ICASE; - - if (newline) - cflags |= REG_NEWLINE; - - res = regcomp(&re->re, pattern, cflags); - - if (res != 0) { - if (err) { - len = regerror(res, &re->re, NULL, 0); - *err = xalloc(len); - - regerror(res, &re->re, *err, len); - } - - uc_value_put(re->header.jso); - - return NULL; - } - - json_object_object_add(re->header.jso, "source", xjs_new_string(pattern)); - json_object_object_add(re->header.jso, "i", xjs_new_boolean(icase)); - json_object_object_add(re->header.jso, "g", xjs_new_boolean(global)); - json_object_object_add(re->header.jso, "s", xjs_new_boolean(newline)); - - return re; -} - -static void -uc_prototype_gc(json_object *jso, void *userdata) -{ - uc_prototype *proto = userdata; - - if (proto->parent) - uc_value_put(proto->parent->header.jso); - - free(proto); -} - -uc_prototype * -uc_prototype_new(uc_prototype *parent) -{ - uc_prototype *proto; - - proto = uc_object_new(UC_OBJ_PROTOTYPE, sizeof(*proto), NULL, uc_prototype_gc); - - if (parent) { - proto->parent = parent; - uc_value_get(parent->header.jso); - } - - return proto; -} - -json_object * -uc_prototype_lookup(uc_prototype *proto, const char *key) -{ - json_object *val; - - for (; proto; proto = proto->parent) - if (json_object_object_get_ex(proto->header.jso, key, &val)) - return val; - - return NULL; -} - -uc_prototype * -uc_protoref_new(json_object *value, uc_prototype *proto) -{ - uc_prototype *ref; - - if (!json_object_is_type(value, json_type_object) && - !json_object_is_type(value, json_type_array)) - return NULL; - - ref = xalloc(sizeof(*ref)); - ref->header.type = UC_OBJ_PROTOTYPE; - ref->header.jso = value; - - if (proto) { - ref->parent = proto; - uc_value_get(proto->header.jso); - } - - json_object_set_serializer(ref->header.jso, NULL, ref, uc_prototype_gc); - - return ref; -} - - -static uc_ressource_types res_types; - -uc_ressource_type * -uc_ressource_type_add(const char *name, uc_prototype *proto, void (*freefn)(void *)) -{ - uc_ressource_type *existing; - - existing = uc_ressource_type_lookup(name); - - if (existing) { - uc_value_put(proto->header.jso); - - return existing; - } - - uc_vector_grow(&res_types); - - res_types.entries[res_types.count].name = name; - res_types.entries[res_types.count].proto = proto; - res_types.entries[res_types.count].free = freefn; - - return &res_types.entries[res_types.count++]; -} - -static uc_ressource_type * -uc_ressource_type_get(size_t type) -{ - return (type < res_types.count) ? &res_types.entries[type] : NULL; -} - -uc_ressource_type * -uc_ressource_type_lookup(const char *name) -{ - size_t i; - - for (i = 0; i < res_types.count; i++) - if (!strcmp(res_types.entries[i].name, name)) - return &res_types.entries[i]; - - return NULL; -} - -static int -uc_ressource_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_ressource *res = json_object_get_userdata(jso); - uc_ressource_type *type = uc_ressource_type_get(res->type); - - return sprintbuf(pb, "%s<%s %p>%s", - strict ? "\"" : "", - type ? type->name : "ressource", - res->data, - strict ? "\"" : ""); -} - -static void -uc_ressource_gc(json_object *jso, void *userdata) -{ - uc_ressource *res = userdata; - uc_ressource_type *type = uc_ressource_type_get(res->type); - - if (type && type->free) - type->free(res->data); - - free(res); -} - -uc_ressource * -uc_ressource_new(json_object *jso, uc_ressource_type *type, void *data) -{ - uc_ressource *res; - - if (!jso) - return NULL; - - res = xalloc(sizeof(*res)); - res->header.type = UC_OBJ_RESSOURCE; - res->header.jso = jso; - - res->type = type - res_types.entries; - res->data = data; - - json_object_set_serializer(res->header.jso, uc_ressource_tostring, res, uc_ressource_gc); - - return res; -} - -void ** -uc_ressource_dataptr(json_object *jso, const char *name) -{ - uc_ressource_type *type; - uc_ressource *res; - - if (!uc_object_is_type(jso, UC_OBJ_RESSOURCE)) - return NULL; - - res = uc_object_as_ressource(jso); - - if (name) { - type = uc_ressource_type_lookup(name); - - if (!type || type != uc_ressource_type_get(res->type)) - return NULL; - } - - return &res->data; -} - -uc_prototype * -uc_ressource_prototype(json_object *jso) -{ - uc_ressource_type *type; - uc_ressource *res; - - if (!uc_object_is_type(jso, UC_OBJ_RESSOURCE)) - return NULL; - - res = uc_object_as_ressource(jso); - type = uc_ressource_type_get(res->type); - - return type ? type->proto : NULL; -} - - -#ifdef __GNUC__ - -__attribute__((destructor)) -static void uc_ressource_types_free(void) -{ - size_t i; - - for (i = 0; i < res_types.count; i++) - uc_value_put(res_types.entries[i].proto->header.jso); - - uc_vector_clear(&res_types); -} - -#endif diff --git a/object.h b/object.h deleted file mode 100644 index 74cc835..0000000 --- a/object.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2020-2021 Jo-Philipp Wich - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef __OBJECT_H_ -#define __OBJECT_H_ - -#include -#include -#include -#include - -#ifdef JSONC - #include -#else - #include -#endif - -#include "source.h" -#include "chunk.h" -#include "util.h" - -typedef enum { - UC_OBJ_INVAL, - UC_OBJ_UPVAL, - UC_OBJ_FUNCTION, - UC_OBJ_CLOSURE, - UC_OBJ_CFUNCTION, - UC_OBJ_REGEXP, - UC_OBJ_PROTOTYPE, - UC_OBJ_RESSOURCE -} uc_objtype_t; - -typedef struct { - uc_objtype_t type; - json_object *jso; -} uc_objhdr; - -typedef struct uc_upvalref { - uc_objhdr header; - size_t slot; - bool closed; - json_object *value; - struct uc_upvalref *next; -} uc_upvalref; - -typedef struct { - uc_objhdr header; - char *name; - bool arrow, vararg; - size_t nargs; - size_t nupvals; - size_t srcpos; - uc_chunk chunk; - uc_source *source; -} uc_function; - -typedef struct { - uc_objhdr header; - uc_function *function; - uc_upvalref **upvals; - bool is_arrow; -} uc_closure; - -struct uc_vm; -typedef json_object *(*uc_cfn_ptr)(struct uc_vm *, size_t); - -typedef struct { - uc_objhdr header; - char *name; - uc_cfn_ptr cfn; -} uc_cfunction; - -typedef struct { - uc_objhdr header; - regex_t re; - char *pattern; - bool icase, newline, global; -} uc_regexp; - -struct uc_prototype { - uc_objhdr header; - struct uc_prototype *parent; -}; - -typedef struct uc_prototype uc_prototype; - -typedef struct { - uc_objhdr header; - size_t type; - void *data; -} uc_ressource; - -typedef struct { - const char *name; - uc_prototype *proto; - void (*free)(void *); -} uc_ressource_type; - -uc_declare_vector(uc_ressource_types, uc_ressource_type); - -uc_upvalref *uc_upvalref_new(size_t slot); -uc_function *uc_function_new(const char *name, size_t line, uc_source *source); -uc_closure *uc_closure_new(uc_function *function, bool arrow_fn); -uc_cfunction *uc_cfunction_new(const char *name, uc_cfn_ptr cfn); -uc_regexp *uc_regexp_new(const char *pattern, bool icase, bool newline, bool global, char **err); -uc_prototype *uc_prototype_new(uc_prototype *parent); -uc_prototype *uc_protoref_new(json_object *value, uc_prototype *proto); -json_object *uc_prototype_lookup(uc_prototype *proto, const char *key); - -uc_ressource_type *uc_ressource_type_add(const char *name, uc_prototype *proto, void (*freefn)(void *)); -uc_ressource_type *uc_ressource_type_lookup(const char *name); - -uc_ressource *uc_ressource_new(json_object *jso, uc_ressource_type *type, void *data); -uc_prototype *uc_ressource_prototype(json_object *jso); -void **uc_ressource_dataptr(json_object *jso, const char *name); - -size_t uc_function_get_srcpos(uc_function *function, size_t off); - -static inline uc_objtype_t -uc_object_type(json_object *jso) -{ - uc_objhdr *hdr = json_object_get_userdata(jso); - - return hdr ? hdr->type : UC_OBJ_INVAL; -} - -static inline bool -uc_object_is_type(json_object *jso, uc_objtype_t type) -{ - return uc_object_type(jso) == type; -} - -static inline uc_upvalref * -uc_object_as_upvalref(json_object *jso) -{ - return json_object_get_userdata(jso); -} - -static inline uc_function * -uc_object_as_function(json_object *jso) -{ - return json_object_get_userdata(jso); -} - -static inline uc_closure * -uc_object_as_closure(json_object *jso) -{ - return json_object_get_userdata(jso); -} - -static inline uc_cfunction * -uc_object_as_cfunction(json_object *jso) -{ - return json_object_get_userdata(jso); -} - -static inline uc_regexp * -uc_object_as_regexp(json_object *jso) -{ - return json_object_get_userdata(jso); -} - -static inline uc_prototype * -uc_object_as_prototype(json_object *jso) -{ - return json_object_get_userdata(jso); -} - -static inline uc_ressource * -uc_object_as_ressource(json_object *jso) -{ - return json_object_get_userdata(jso); -} - -static inline bool -uc_object_is_callable(json_object *jso) -{ - switch (uc_object_type(jso)) { - case UC_OBJ_CLOSURE: - case UC_OBJ_CFUNCTION: - return true; - - default: - return false; - } -} - -#endif /* __OBJECT_H_ */ diff --git a/tests/custom/00_syntax/21_regex_literals b/tests/custom/00_syntax/21_regex_literals index 3af53bb..6d85e97 100644 --- a/tests/custom/00_syntax/21_regex_literals +++ b/tests/custom/00_syntax/21_regex_literals @@ -23,7 +23,7 @@ regular expression engine. Testing regular expression type. -- Expect stdout -- -object +regexp -- End -- -- Testcase -- diff --git a/types.c b/types.c new file mode 100644 index 0000000..de2860f --- /dev/null +++ b/types.c @@ -0,0 +1,1755 @@ +/* + * Copyright (C) 2021 Jo-Philipp Wich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "util.h" +#include "vm.h" + +uc_type_t +ucv_type(uc_value_t *uv) +{ + uc_type_t type = ((uintptr_t)uv & 3); + + if (type == UC_NULL && uv != NULL) + type = uv->type; + + return type; +} + +const char * +ucv_typename(uc_value_t *uv) +{ + switch (ucv_type(uv)) { + case UC_NULL: return "null"; + case UC_INTEGER: return "integer"; + case UC_BOOLEAN: return "boolean"; + case UC_STRING: return "string"; + case UC_DOUBLE: return "double"; + case UC_ARRAY: return "array"; + case UC_OBJECT: return "object"; + case UC_REGEXP: return "regexp"; + case UC_FUNCTION: return "function"; + case UC_CFUNCTION: return "cfunction"; + case UC_CLOSURE: return "closure"; + case UC_UPVALUE: return "upvalue"; + case UC_RESSOURCE: return "ressource"; + } + + return "unknown"; +} + +static uc_ressource_type_t * +ucv_ressource_type_get(size_t type); + +static void +ucv_unref(uc_weakref_t *ref) +{ + ref->prev->next = ref->next; + ref->next->prev = ref->prev; +} + +static void +ucv_ref(uc_weakref_t *head, uc_weakref_t *item) +{ + item->next = head->next; + item->prev = head; + head->next->prev = item; + head->next = item; +} + +#if 0 +static uc_weakref_t * +ucv_get_weakref(uc_value_t *uv) +{ + switch (ucv_type(uv)) { + case UC_ARRAY: + return &((uc_array_t *)uv)->ref; + + case UC_OBJECT: + return &((uc_object_t *)uv)->ref; + + case UC_CLOSURE: + return &((uc_closure_t *)uv)->ref; + + default: + return NULL; + } +} +#endif + +static void +ucv_put_value(uc_value_t *uv, bool retain) +{ + if (uv == NULL || (uintptr_t)uv & 3) + return; + + assert(uv->refcount > 0); + + if (uv->refcount > 0) + uv->refcount--; + + if (uv->refcount == 0) + ucv_free(uv, retain); +} + +static void +ucv_gc_mark(uc_value_t *uv); + +static void +ucv_gc_mark(uc_value_t *uv) +{ + uc_function_t *function; + uc_closure_t *closure; + uc_upvalref_t *upval; + uc_object_t *object; + uc_array_t *array; + struct lh_entry *entry; + size_t i; + + switch (ucv_type(uv)) { + case UC_ARRAY: + array = (uc_array_t *)uv; + + ucv_gc_mark(array->proto); + + for (i = 0; i < array->count; i++) + ucv_gc_mark(array->entries[i]); + + if (array->ref.next) + ucv_set_mark(uv); + + break; + + case UC_OBJECT: + object = (uc_object_t *)uv; + + ucv_gc_mark(object->proto); + + lh_foreach(object->table, entry) + ucv_gc_mark((uc_value_t *)lh_entry_v(entry)); + + if (object->ref.next) + ucv_set_mark(uv); + + break; + + case UC_CLOSURE: + closure = (uc_closure_t *)uv; + function = closure->function; + + for (i = 0; i < function->nupvals; i++) + ucv_gc_mark((uc_value_t *)closure->upvals[i]); + + ucv_gc_mark((uc_value_t *)function); + + if (closure->ref.next) + ucv_set_mark(uv); + + break; + + case UC_UPVALUE: + upval = (uc_upvalref_t *)uv; + ucv_gc_mark(upval->value); + break; + + default: + break; + } +} + +void +ucv_free(uc_value_t *uv, bool retain) +{ + uc_ressource_type_t *restype; + uc_ressource_t *ressource; + uc_function_t *function; + uc_closure_t *closure; + uc_upvalref_t *upval; + uc_regexp_t *regexp; + uc_object_t *object; + uc_array_t *array; + uc_weakref_t *ref; + size_t i; + + if (uv == NULL || (uintptr_t)uv & 3) + return; + + if (uv->mark) + return; + + uv->mark = true; + + ref = NULL; + + switch (uv->type) { + case UC_ARRAY: + array = (uc_array_t *)uv; + ref = &array->ref; + ucv_put_value(array->proto, retain); + + for (i = 0; i < array->count; i++) + ucv_put_value(array->entries[i], retain); + + uc_vector_clear(array); + break; + + case UC_OBJECT: + object = (uc_object_t *)uv; + ref = &object->ref; + ucv_put_value(object->proto, retain); + lh_table_free(object->table); + break; + + case UC_REGEXP: + regexp = (uc_regexp_t *)uv; + regfree(®exp->regexp); + break; + + case UC_FUNCTION: + function = (uc_function_t *)uv; + uc_chunk_free(&function->chunk); + uc_source_put(function->source); + break; + + case UC_CLOSURE: + closure = (uc_closure_t *)uv; + function = closure->function; + ref = &closure->ref; + + for (i = 0; i < function->nupvals; i++) + ucv_put_value((uc_value_t *)closure->upvals[i], retain); + + ucv_put_value((uc_value_t *)function, retain); + break; + + case UC_RESSOURCE: + ressource = (uc_ressource_t *)uv; + restype = ucv_ressource_type_get(ressource->type); + + if (restype && restype->free) + restype->free(ressource->data); + + break; + + case UC_UPVALUE: + upval = (uc_upvalref_t *)uv; + ucv_put_value(upval->value, retain); + break; + } + + if (!ref || !retain) { + if (ref && ref->prev && ref->next) + ucv_unref(ref); + + free(uv); + } + else { + uv->type = UC_NULL; + } +} + +void +ucv_put(uc_value_t *uv) +{ + ucv_put_value(uv, false); +} + +uc_value_t * +ucv_get(uc_value_t *uv) +{ + if (uv == NULL || (uintptr_t)uv & 3) + return uv; + + assert(uv->refcount < 0x03ffffff); + + uv->refcount++; + + return uv; +} + +uc_value_t * +ucv_boolean_new(bool val) +{ + uintptr_t pv = UC_BOOLEAN | (val << 2); + + return (uc_value_t *)pv; +} + +bool +ucv_boolean_get(uc_value_t *uv) +{ + uintptr_t pv = (uintptr_t)uv; + + if ((pv & 3) == UC_BOOLEAN) + return (pv >> 2) & 1; + + return false; +} + + +uc_value_t * +ucv_string_new(const char *str) +{ + return ucv_string_new_length(str, strlen(str)); +} + +uc_value_t * +ucv_string_new_length(const char *str, size_t length) +{ + uc_string_t *ustr; + uintptr_t pv; + size_t i; + char *s; + + if ((length + 1) < sizeof(void *)) { + pv = UC_STRING | (length << 2); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + s = (char *)&pv + 1; +#else + s = (char *)&pv; +#endif + + for (i = 0; i < length; i++) + s[i] = str[i]; + + return (uc_value_t *)pv; + } + + ustr = xalloc(sizeof(*ustr) + length + 1); + ustr->header.type = UC_STRING; + ustr->header.refcount = 1; + ustr->length = length; + memcpy(ustr->str, str, length); + + return &ustr->header; +} + +uc_stringbuf_t * +ucv_stringbuf_new(void) +{ + uc_stringbuf_t *sb = xprintbuf_new(); + uc_string_t ustr = { + .header = { + .type = UC_STRING, + .refcount = 1 + } + }; + + printbuf_memappend_fast(sb, (char *)&ustr, sizeof(ustr)); + + return sb; +} + +uc_value_t * +ucv_stringbuf_finish(uc_stringbuf_t *sb) +{ + uc_string_t *ustr = (uc_string_t *)sb->buf; + + ustr->length = printbuf_length(sb) - offsetof(uc_string_t, str); + + free(sb); + + return &ustr->header; +} + +char * +_ucv_string_get(uc_value_t **uv) +{ + uc_string_t *str; + + switch ((uintptr_t)*uv & 3) { + case UC_STRING: +#if __BYTE_ORDER == __LITTLE_ENDIAN + return (char *)uv + 1; +#else + return (char *)uv; +#endif + + case UC_NULL: + if (*uv != NULL && (*uv)->type == UC_STRING) { + str = (uc_string_t *)*uv; + + return str->str; + } + } + + return NULL; +} + +size_t +ucv_string_length(uc_value_t *uv) +{ + uc_string_t *str = (uc_string_t *)uv; + uintptr_t pv = (uintptr_t)uv; + + if ((pv & 3) == UC_STRING) + return (pv & 0xff) >> 2; + else if (uv != NULL && uv->type == UC_STRING) + return str->length; + + return 0; +} + + +uc_value_t * +ucv_int64_new(int64_t n) +{ + uint64_t uval = (n < 0) ? ((n > INT64_MIN) ? (~n + 1) : INT64_MAX) : n; + uint64_t max = (1ULL << ((sizeof(void *) * 8) - 3)) - 1; + uc_integer_t *integer; + uintptr_t pv; + + if (uval <= max) { + pv = UC_INTEGER | ((n < 0) << 2) | (uval << 3); + + return (uc_value_t *)pv; + } + + integer = xalloc(sizeof(*integer)); + integer->header.type = UC_INTEGER; + integer->header.refcount = 1; + integer->header.u64 = 0; + integer->i.s64 = n; + + return &integer->header; +} + +uc_value_t * +ucv_uint64_new(uint64_t n) +{ + uint64_t max = (1ULL << ((sizeof(void *) * 8) - 3)) - 1; + uc_integer_t *integer; + uintptr_t pv; + + if (n <= max) { + pv = UC_INTEGER | (n << 3); + + return (uc_value_t *)pv; + } + + integer = xalloc(sizeof(*integer)); + integer->header.type = UC_INTEGER; + integer->header.refcount = 1; + integer->header.u64 = 1; + integer->i.u64 = n; + + return &integer->header; +} + +uint64_t +ucv_uint64_get(uc_value_t *uv) +{ + uintptr_t pv = (uintptr_t)uv; + uc_integer_t *integer; + + errno = 0; + + if ((pv & 3) == UC_INTEGER) { + if (((pv >> 2) & 1) == 0) + return (uint64_t)(pv >> 3); + + errno = ERANGE; + + return 0; + } + else if (uv != NULL && uv->type == UC_INTEGER) { + integer = (uc_integer_t *)uv; + + if (integer->header.u64) + return integer->i.u64; + + if (integer->i.s64 >= 0) + return (uint64_t)integer->i.s64; + + errno = ERANGE; + + return 0; + } + + errno = EINVAL; + + return 0; +} + +int64_t +ucv_int64_get(uc_value_t *uv) +{ + uintptr_t pv = (uintptr_t)uv; + uc_integer_t *integer; + + errno = 0; + + if ((pv & 3) == UC_INTEGER) { + if (((pv >> 2) & 1) == 0) + return (int64_t)(pv >> 3); + + return -(int64_t)(pv >> 3); + } + else if (uv != NULL && uv->type == UC_INTEGER) { + integer = (uc_integer_t *)uv; + + if (integer->header.u64 && integer->i.u64 <= INT64_MAX) + return (int64_t)integer->i.u64; + + if (!integer->header.u64) + return integer->i.s64; + + errno = ERANGE; + + return 0; + } + + errno = EINVAL; + + return 0; +} + + +uc_value_t * +ucv_double_new(double d) +{ + uc_double_t *dbl; + + dbl = xalloc(sizeof(*dbl)); + dbl->header.type = UC_DOUBLE; + dbl->header.refcount = 1; + dbl->dbl = d; + + return &dbl->header; +} + +double +ucv_double_get(uc_value_t *uv) +{ + uc_double_t *dbl; + + errno = 0; + + if (ucv_type(uv) != UC_DOUBLE) { + errno = EINVAL; + + return NAN; + } + + dbl = (uc_double_t *)uv; + + return dbl->dbl; +} + + +uc_value_t * +ucv_array_new(uc_vm *vm) +{ + return ucv_array_new_length(vm, 0); +} + +uc_value_t * +ucv_array_new_length(uc_vm *vm, size_t length) +{ + uc_array_t *array; + + /* XXX */ + length = 0; + + array = xalloc(sizeof(*array) + length * sizeof(array->entries[0])); + array->header.type = UC_ARRAY; + array->header.refcount = 1; + + if (length > 0) + array->count = length; + + uc_vector_grow(array); + + if (vm) + ucv_ref(&vm->values, &array->ref); + + return &array->header; +} + +uc_value_t * +ucv_array_pop(uc_value_t *uv) +{ + uc_array_t *array = (uc_array_t *)uv; + uc_value_t *item; + + if (ucv_type(uv) != UC_ARRAY || array->count == 0) + return NULL; + + item = ucv_get(array->entries[array->count - 1]); + + ucv_array_delete(uv, array->count - 1, 1); + + return item; +} + +uc_value_t * +ucv_array_push(uc_value_t *uv, uc_value_t *item) +{ + uc_array_t *array = (uc_array_t *)uv; + + if (ucv_type(uv) != UC_ARRAY) + return NULL; + + ucv_array_set(uv, array->count, item); + + return item; +} + +uc_value_t * +ucv_array_shift(uc_value_t *uv) +{ + uc_array_t *array = (uc_array_t *)uv; + uc_value_t *item; + + if (ucv_type(uv) != UC_ARRAY || array->count == 0) + return NULL; + + item = ucv_get(array->entries[0]); + + ucv_array_delete(uv, 0, 1); + + return item; +} + +uc_value_t * +ucv_array_unshift(uc_value_t *uv, uc_value_t *item) +{ + uc_array_t *array = (uc_array_t *)uv; + size_t i; + + if (ucv_type(uv) != UC_ARRAY || array->count == 0) + return NULL; + + array->count++; + uc_vector_grow(array); + + for (i = array->count; i > 1; i--) + array->entries[i - 1] = array->entries[i - 2]; + + array->entries[0] = item; + + return item; +} + +void +ucv_array_sort(uc_value_t *uv, int (*cmp)(const void *, const void *)) +{ + uc_array_t *array = (uc_array_t *)uv; + + if (ucv_type(uv) != UC_ARRAY || array->count <= 1) + return; + + qsort(array->entries, array->count, sizeof(array->entries[0]), cmp); +} + +bool +ucv_array_delete(uc_value_t *uv, size_t offset, size_t count) +{ + uc_array_t *array = (uc_array_t *)uv; + size_t i; + + if (ucv_type(uv) != UC_ARRAY || array->count == 0) + return false; + + if (offset >= array->count) + return false; + + if ((offset + count) < offset) + return false; + + if ((offset + count) > array->count) + count = array->count - offset; + + for (i = 0; i < count; i++) + ucv_put(array->entries[offset + i]); + + memmove(&array->entries[offset], + &array->entries[offset + count], + (array->count - (offset + count)) * sizeof(array->entries[0])); + + array->count -= count; + + uc_vector_grow(array); + + return true; +} + +bool +ucv_array_set(uc_value_t *uv, size_t index, uc_value_t *item) +{ + uc_array_t *array = (uc_array_t *)uv; + + if (ucv_type(uv) != UC_ARRAY) + return false; + + if (index >= array->count) { + array->count = index + 1; + uc_vector_grow(array); + } + else { + ucv_put(array->entries[index]); + } + + array->entries[index] = item; + + return true; +} + +uc_value_t * +ucv_array_get(uc_value_t *uv, size_t index) +{ + uc_array_t *array = (uc_array_t *)uv; + + if (ucv_type(uv) != UC_ARRAY) + return NULL; + + if (index >= array->count) + return NULL; + + return array->entries[index]; +} +size_t +ucv_array_length(uc_value_t *uv) +{ + uc_array_t *array = (uc_array_t *)uv; + + if (ucv_type(uv) != UC_ARRAY) + return 0; + + return array->count; +} + + +static void +ucv_free_object_entry(struct lh_entry *entry) +{ + free(lh_entry_k(entry)); + ucv_put(lh_entry_v(entry)); +} + +uc_value_t * +ucv_object_new(uc_vm *vm) +{ + struct lh_table *table; + uc_object_t *object; + + table = lh_kchar_table_new(16, ucv_free_object_entry); + + if (!table) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + object = xalloc(sizeof(*object)); + object->header.type = UC_OBJECT; + object->header.refcount = 1; + object->table = table; + + if (vm) + ucv_ref(&vm->values, &object->ref); + + return &object->header; +} + +bool +ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) +{ + uc_object_t *object = (uc_object_t *)uv; + struct lh_entry *existing_entry; + uc_value_t *existing_value; + unsigned long hash; + + if (ucv_type(uv) != UC_OBJECT) + return false; + + hash = lh_get_hash(object->table, (const void *)key); + existing_entry = lh_table_lookup_entry_w_hash(object->table, (const void *)key, hash); + + if (existing_entry == NULL) { + return (lh_table_insert_w_hash(object->table, xstrdup(key), val, hash, 0) == 0); + } + + existing_value = (uc_value_t *)lh_entry_v(existing_entry); + + if (existing_value) + ucv_put(existing_value); + + existing_entry->v = val; + + return true; +} + +bool +ucv_object_delete(uc_value_t *uv, const char *key) +{ + uc_object_t *object = (uc_object_t *)uv; + + if (ucv_type(uv) != UC_OBJECT) + return false; + + return (lh_table_delete(object->table, key) == 0); +} + +uc_value_t * +ucv_object_get(uc_value_t *uv, const char *key, bool *found) +{ + uc_object_t *object = (uc_object_t *)uv; + uc_value_t *val = NULL; + bool rv; + + if (found != NULL) + *found = false; + + if (ucv_type(uv) != UC_OBJECT) + return NULL; + + rv = lh_table_lookup_ex(object->table, (const void *)key, (void **)&val); + + if (found != NULL) + *found = rv; + + return val; +} + +size_t +ucv_object_length(uc_value_t *uv) +{ + uc_object_t *object = (uc_object_t *)uv; + + if (ucv_type(uv) != UC_OBJECT) + return 0; + + return lh_table_length(object->table); +} + + +uc_value_t * +ucv_function_new(const char *name, size_t srcpos, uc_source *source) +{ + size_t namelen = 0; + uc_function_t *fn; + + if (name) + namelen = strlen(name); + + fn = xalloc(sizeof(*fn) + namelen + 1); + fn->header.type = UC_FUNCTION; + fn->header.refcount = 1; + + if (name) + strcpy(fn->name, name); + + fn->nargs = 0; + fn->nupvals = 0; + fn->srcpos = srcpos; + fn->source = uc_source_get(source); + fn->vararg = false; + + uc_chunk_init(&fn->chunk); + + return &fn->header; +} + +size_t +ucv_function_srcpos(uc_value_t *uv, size_t off) +{ + uc_function_t *fn = (uc_function_t *)uv; + size_t pos; + + if (ucv_type(uv) != UC_FUNCTION) + return 0; + + pos = uc_chunk_debug_get_srcpos(&fn->chunk, off); + + return pos ? fn->srcpos + pos : 0; +} + + +uc_value_t * +ucv_cfunction_new(const char *name, uc_cfn_ptr_t fptr) +{ + uc_cfunction_t *cfn; + size_t namelen = 0; + + if (name) + namelen = strlen(name); + + cfn = xalloc(sizeof(*cfn) + namelen + 1); + cfn->header.type = UC_CFUNCTION; + cfn->header.refcount = 1; + + if (name) + strcpy(cfn->name, name); + + cfn->cfn = fptr; + + return &cfn->header; +} + + +uc_value_t * +ucv_closure_new(uc_vm *vm, uc_function_t *function, bool arrow_fn) +{ + uc_closure_t *closure; + + closure = xalloc(sizeof(*closure) + (sizeof(uc_upvalref_t *) * function->nupvals)); + closure->header.type = UC_CLOSURE; + closure->header.refcount = 1; + closure->function = function; + closure->is_arrow = arrow_fn; + closure->upvals = function->nupvals ? ((void *)closure + ALIGN(sizeof(*closure))) : NULL; + + if (vm) + ucv_ref(&vm->values, &closure->ref); + + return &closure->header; +} + + +static uc_ressource_types_t res_types; + +uc_ressource_type_t * +ucv_ressource_type_add(const char *name, uc_value_t *proto, void (*freefn)(void *)) +{ + uc_ressource_type_t *existing; + + existing = ucv_ressource_type_lookup(name); + + if (existing) { + ucv_put(proto); + + return existing; + } + + uc_vector_grow(&res_types); + + res_types.entries[res_types.count].name = name; + res_types.entries[res_types.count].proto = proto; + res_types.entries[res_types.count].free = freefn; + + return &res_types.entries[res_types.count++]; +} + +static uc_ressource_type_t * +ucv_ressource_type_get(size_t type) +{ + return (type < res_types.count) ? &res_types.entries[type] : NULL; +} + +uc_ressource_type_t * +ucv_ressource_type_lookup(const char *name) +{ + size_t i; + + for (i = 0; i < res_types.count; i++) + if (!strcmp(res_types.entries[i].name, name)) + return &res_types.entries[i]; + + return NULL; +} + + +uc_value_t * +ucv_ressource_new(uc_ressource_type_t *type, void *data) +{ + uc_ressource_t *res; + + res = xalloc(sizeof(*res)); + res->header.type = UC_RESSOURCE; + res->header.refcount = 1; + res->type = type - res_types.entries; + res->data = data; + + return &res->header; +} + +void ** +ucv_ressource_dataptr(uc_value_t *uv, const char *name) +{ + uc_ressource_t *res = (uc_ressource_t *)uv; + uc_ressource_type_t *type; + + if (ucv_type(uv) != UC_RESSOURCE) + return NULL; + + if (name) { + type = ucv_ressource_type_lookup(name); + + if (!type || type != ucv_ressource_type_get(res->type)) + return NULL; + } + + return &res->data; +} + + +uc_value_t * +ucv_regexp_new(const char *pattern, bool icase, bool newline, bool global, char **error) +{ + int cflags = REG_EXTENDED, res; + uc_regexp_t *re; + size_t len; + + re = xalloc(sizeof(*re) + strlen(pattern) + 1); + re->header.type = UC_REGEXP; + re->header.refcount = 1; + re->icase = icase; + re->global = global; + re->newline = newline; + strcpy(re->source, pattern); + + if (icase) + cflags |= REG_ICASE; + + if (newline) + cflags |= REG_NEWLINE; + + res = regcomp(&re->regexp, pattern, cflags); + + if (res != 0) { + if (error) { + len = regerror(res, &re->regexp, NULL, 0); + *error = xalloc(len); + + regerror(res, &re->regexp, *error, len); + } + + free(re); + + return NULL; + } + + /* + json_object_object_add(re->header.jso, "source", xjs_new_string(pattern)); + json_object_object_add(re->header.jso, "i", xjs_new_boolean(icase)); + json_object_object_add(re->header.jso, "g", xjs_new_boolean(global)); + json_object_object_add(re->header.jso, "s", xjs_new_boolean(newline)); + */ + + return &re->header; +} + + +uc_value_t * +ucv_upvalref_new(size_t slot) +{ + uc_upvalref_t *up; + + up = xalloc(sizeof(*up)); + up->header.type = UC_UPVALUE; + up->header.refcount = 1; + up->slot = slot; + + return &up->header; +} + + +uc_value_t * +ucv_prototype_get(uc_value_t *uv) +{ + uc_ressource_type_t *restype; + uc_ressource_t *ressource; + uc_object_t *object; + uc_array_t *array; + + switch (ucv_type(uv)) { + case UC_ARRAY: + array = (uc_array_t *)uv; + + return array->proto; + + case UC_OBJECT: + object = (uc_object_t *)uv; + + return object->proto; + + case UC_RESSOURCE: + ressource = (uc_ressource_t *)uv; + restype = ucv_ressource_type_get(ressource->type); + + return restype ? restype->proto : NULL; + + default: + return NULL; + } +} + +bool +ucv_prototype_set(uc_value_t *uv, uc_value_t *proto) +{ + uc_object_t *object; + uc_array_t *array; + + if (ucv_type(proto) != UC_OBJECT) + return false; + + switch (ucv_type(uv)) { + case UC_ARRAY: + array = (uc_array_t *)uv; + array->proto = proto; + + return true; + + case UC_OBJECT: + object = (uc_object_t *)uv; + object->proto = proto; + + return true; + + default: + return false; + } +} + +uc_value_t * +ucv_property_get(uc_value_t *uv, const char *key) +{ + uc_value_t *val; + bool found; + + for (; uv; uv = ucv_prototype_get(uv)) { + val = ucv_object_get(uv, key, &found); + + if (found) + return val; + } + + return NULL; +} + + +uc_value_t * +ucv_from_json(uc_vm *vm, json_object *jso) +{ + //uc_array_t *arr; + uc_value_t *uv; + int64_t n; + size_t i; + + switch (json_object_get_type(jso)) { + case json_type_null: + return NULL; + + case json_type_boolean: + return ucv_boolean_new(json_object_get_boolean(jso)); + + case json_type_double: + return ucv_double_new(json_object_get_double(jso)); + + case json_type_int: + n = json_object_get_int64(jso); + + if (n == INT64_MAX) + return ucv_uint64_new(json_object_get_uint64(jso)); + + return ucv_int64_new(n); + + case json_type_object: + uv = ucv_object_new(vm); + + json_object_object_foreach(jso, key, val) + ucv_object_add(uv, key, ucv_from_json(vm, val)); + + return uv; + + case json_type_array: + /* XXX + arr = (uc_array_t *)ucv_array_new_length(vm, json_object_array_length(jso)); + + for (i = 0; i < arr->count; i++) + arr->entries[i] = ucv_from_json(vm, json_object_array_get_idx(jso, i)); + + return &arr->header; + */ + uv = ucv_array_new(vm); + + for (i = 0; i < json_object_array_length(jso); i++) + ucv_array_push(uv, ucv_from_json(vm, json_object_array_get_idx(jso, i))); + + return uv; + + case json_type_string: + return ucv_string_new_length(json_object_get_string(jso), json_object_get_string_len(jso)); + } + + return NULL; +} + +json_object * +ucv_to_json(uc_value_t *uv) +{ + uc_regexp_t *regexp; + uc_array_t *array; + json_object *jso; + size_t i; + char *s; + + switch (ucv_type(uv)) { + case UC_BOOLEAN: + return json_object_new_boolean(ucv_boolean_get(uv)); + + case UC_INTEGER: + if (ucv_is_u64(uv)) + return json_object_new_uint64(ucv_uint64_get(uv)); + + return json_object_new_int64(ucv_int64_get(uv)); + + case UC_DOUBLE: + return json_object_new_double(ucv_double_get(uv)); + + case UC_STRING: + return json_object_new_string_len(ucv_string_get(uv), ucv_string_length(uv)); + + case UC_ARRAY: + array = (uc_array_t *)uv; + jso = json_object_new_array_ext(array->count); + + for (i = 0; i < array->count; i++) + json_object_array_put_idx(jso, i, ucv_to_json(array->entries[i])); + + return jso; + + case UC_OBJECT: + jso = json_object_new_object(); + + ucv_object_foreach(uv, key, val) + json_object_object_add(jso, key, ucv_to_json(val)); + + return jso; + + case UC_REGEXP: + regexp = (uc_regexp_t *)uv; + i = asprintf(&s, "/%s/%s%s%s", + regexp->source, + regexp->global ? "g" : "", + regexp->icase ? "i" : "", + regexp->newline ? "s" : ""); + + if (i <= 0) + return NULL; + + jso = json_object_new_string_len(s, i); + + free(s); + + return jso; + + case UC_CLOSURE: + case UC_CFUNCTION: + case UC_FUNCTION: + case UC_RESSOURCE: + case UC_UPVALUE: + case UC_NULL: + return NULL; + } + + return NULL; +} + +static void +ucv_to_string_json_encoded(uc_stringbuf_t *pb, const char *s, size_t len, bool regexp) +{ + size_t i; + + if (!regexp) + ucv_stringbuf_append(pb, "\""); + + for (i = 0; i < len; i++, s++) { + switch (*s) { + case '"': + ucv_stringbuf_append(pb, "\\\""); + break; + + case '\\': + ucv_stringbuf_append(pb, "\\\\"); + break; + + case '\b': + ucv_stringbuf_append(pb, "\\b"); + break; + + case '\f': + ucv_stringbuf_append(pb, "\\f"); + break; + + case '\n': + ucv_stringbuf_append(pb, "\\n"); + break; + + case '\r': + ucv_stringbuf_append(pb, "\\r"); + break; + + case '\t': + ucv_stringbuf_append(pb, "\\t"); + break; + + case '/': + if (regexp) + ucv_stringbuf_append(pb, "\\"); + + ucv_stringbuf_append(pb, "/"); + break; + + default: + if (*s < 0x20) + ucv_stringbuf_printf(pb, "\\u%04x", *s); + else + ucv_stringbuf_addstr(pb, s, 1); + break; + } + } + + if (!regexp) + ucv_stringbuf_append(pb, "\""); +} + +static bool +ucv_call_tostring(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) +{ + uc_value_t *proto = ucv_prototype_get(uv); + uc_value_t *tostr = ucv_object_get(proto, "tostring", NULL); + uc_value_t *str; + + if (!ucv_is_callable(tostr)) + return false; + + uc_vm_stack_push(vm, ucv_get(uv)); + uc_vm_stack_push(vm, ucv_get(tostr)); + + if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) + return false; + + str = uc_vm_stack_pop(vm); + + if (ucv_type(str) == UC_STRING) { + if (json) + ucv_to_string_json_encoded(pb, ucv_string_get(str), ucv_string_length(str), false); + else + ucv_stringbuf_addstr(pb, ucv_string_get(str), ucv_string_length(str)); + } + else if (json) { + ucv_stringbuf_append(pb, "\"\""); + } + + ucv_put(str); + + return true; +} + +void +_ucv_stringbuf_append(uc_stringbuf_t *pb, const char *str, size_t len) +{ + printbuf_memappend_fast(pb, str, len); +} + +void +ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) +{ + uc_ressource_type_t *restype; + uc_ressource_t *ressource; + uc_cfunction_t *cfunction; + uc_function_t *function; + uc_closure_t *closure; + uc_regexp_t *regexp; + uc_value_t *argname; + uc_array_t *array; + size_t i; + double d; + + if (ucv_is_marked(uv)) { + ucv_stringbuf_append(pb, "null"); + + return; + } + + if (vm != NULL && ucv_call_tostring(vm, pb, uv, json)) + return; + + ucv_set_mark(uv); + + switch (ucv_type(uv)) { + case UC_NULL: + ucv_stringbuf_append(pb, "null"); + break; + + case UC_BOOLEAN: + if (ucv_boolean_get(uv)) + ucv_stringbuf_append(pb, "true"); + else + ucv_stringbuf_append(pb, "false"); + break; + + case UC_INTEGER: + if (ucv_is_u64(uv)) + ucv_stringbuf_printf(pb, "%" PRIu64, ucv_uint64_get(uv)); + else + ucv_stringbuf_printf(pb, "%" PRId64, ucv_int64_get(uv)); + break; + + case UC_DOUBLE: + d = ucv_double_get(uv); + + if (json && isnan(d)) + ucv_stringbuf_append(pb, "\"NaN\""); + else if (json && d == INFINITY) + ucv_stringbuf_append(pb, "1e309"); + else if (json && d == -INFINITY) + ucv_stringbuf_append(pb, "-1e309"); + else if (isnan(d)) + ucv_stringbuf_append(pb, "NaN"); + else if (d == INFINITY) + ucv_stringbuf_append(pb, "Infinity"); + else if (d == -INFINITY) + ucv_stringbuf_append(pb, "-Infinity"); + else + ucv_stringbuf_printf(pb, "%g", d); + + break; + + case UC_STRING: + if (json) + ucv_to_string_json_encoded(pb, ucv_string_get(uv), ucv_string_length(uv), false); + else + ucv_stringbuf_addstr(pb, ucv_string_get(uv), ucv_string_length(uv)); + break; + + case UC_ARRAY: + array = (uc_array_t *)uv; + + ucv_stringbuf_append(pb, "["); + + for (i = 0; i < array->count; i++) { + if (i) + ucv_stringbuf_append(pb, ", "); + else + ucv_stringbuf_append(pb, " "); + + ucv_to_stringbuf(vm, pb, array->entries[i], true); + } + + ucv_stringbuf_append(pb, " ]"); + break; + + case UC_OBJECT: + ucv_stringbuf_append(pb, "{"); + + i = 0; + ucv_object_foreach(uv, key, val) { + if (i++) + ucv_stringbuf_append(pb, ", "); + else + ucv_stringbuf_append(pb, " "); + + ucv_to_string_json_encoded(pb, key, strlen(key), false); + ucv_stringbuf_append(pb, ": "); + ucv_to_stringbuf(vm, pb, val, true); + } + + ucv_stringbuf_append(pb, " }"); + break; + + case UC_REGEXP: + regexp = (uc_regexp_t *)uv; + + if (json) + ucv_stringbuf_append(pb, "\""); + + ucv_stringbuf_append(pb, "/"); + ucv_to_string_json_encoded(pb, regexp->source, strlen(regexp->source), true); + ucv_stringbuf_append(pb, "/"); + + if (regexp->global) + ucv_stringbuf_append(pb, "g"); + + if (regexp->icase) + ucv_stringbuf_append(pb, "i"); + + if (regexp->newline) + ucv_stringbuf_append(pb, "s"); + + if (json) + ucv_stringbuf_append(pb, "\""); + + break; + + case UC_CLOSURE: + closure = (uc_closure_t *)uv; + function = closure->function; + + if (json) + ucv_stringbuf_append(pb, "\""); + + if (!closure->is_arrow) { + ucv_stringbuf_append(pb, "function"); + + if (function->name[0]) { + ucv_stringbuf_append(pb, " "); + ucv_stringbuf_addstr(pb, function->name, strlen(function->name)); + } + } + + ucv_stringbuf_append(pb, "("); + + for (i = 1; i <= function->nargs; i++) { + argname = uc_chunk_debug_get_variable(&function->chunk, i - 1, i, false); + + if (i > 1) + ucv_stringbuf_append(pb, ", "); + + if (i == function->nargs && function->vararg) + ucv_stringbuf_append(pb, "..."); + + if (argname) + ucv_stringbuf_addstr(pb, ucv_string_get(argname), ucv_string_length(argname)); + else + ucv_stringbuf_printf(pb, "[arg%zu]", i); + + ucv_put(argname); + } + + ucv_stringbuf_printf(pb, ")%s { ... }%s", + closure->is_arrow ? " =>" : "", + json ? "\"" : ""); + + break; + + case UC_CFUNCTION: + cfunction = (uc_cfunction_t *)uv; + + ucv_stringbuf_printf(pb, "%sfunction%s%s(...) { [native code] }%s", + json ? "\"" : "", + cfunction->name ? " " : "", + cfunction->name ? cfunction->name : "", + json ? "\"" : ""); + + break; + + case UC_FUNCTION: + ucv_stringbuf_printf(pb, "%s%s", + json ? "\"" : "", + uv, + json ? "\"" : ""); + + break; + + case UC_RESSOURCE: + ressource = (uc_ressource_t *)uv; + restype = ucv_ressource_type_get(ressource->type); + + ucv_stringbuf_printf(pb, "%s<%s %p>%s", + json ? "\"" : "", + restype ? restype->name : "ressource", + ressource->data, + json ? "\"" : ""); + + break; + + case UC_UPVALUE: + ucv_stringbuf_printf(pb, "%s%s", + json ? "\"" : "", + uv, + json ? "\"" : ""); + + break; + } + + ucv_clear_mark(uv); +} + +static char * +ucv_to_string_any(uc_vm *vm, uc_value_t *uv, bool json) +{ + uc_stringbuf_t *pb = xprintbuf_new(); + char *rv; + + ucv_to_stringbuf(vm, pb, uv, json); + + rv = pb->buf; + + free(pb); + + return rv; +} + +char * +ucv_to_string(uc_vm *vm, uc_value_t *uv) +{ + return ucv_to_string_any(vm, uv, false); +} + +char * +ucv_to_jsonstring(uc_vm *vm, uc_value_t *uv) +{ + return ucv_to_string_any(vm, uv, true); +} + +bool +ucv_equal(uc_value_t *uv1, uc_value_t *uv2) +{ + uc_type_t t1 = ucv_type(uv1); + uc_type_t t2 = ucv_type(uv2); + uint64_t u1, u2; + int64_t n1, n2; + bool b1, b2; + + if (t1 != t2) + return false; + + if (uv1 == uv2) + return true; + + switch (t1) { + case UC_NULL: + return true; + + case UC_BOOLEAN: + return ucv_boolean_get(uv1) == ucv_boolean_get(uv2); + + case UC_DOUBLE: + return ucv_double_get(uv1) == ucv_double_get(uv2); + + case UC_INTEGER: + n1 = ucv_int64_get(uv1); + b1 = (errno == 0); + + n2 = ucv_int64_get(uv2); + b2 = (errno == 0); + + if (b1 && b2) + return (n1 == n2); + + u1 = ucv_uint64_get(uv1); + b1 = (errno == 0); + + u2 = ucv_uint64_get(uv2); + b2 = (errno == 0); + + if (b1 && b2) + return (u1 == u2); + + return false; + + case UC_STRING: + u1 = ucv_string_length(uv1); + u2 = ucv_string_length(uv2); + + if (u1 != u2) + return false; + + return (memcmp(ucv_string_get(uv1), ucv_string_get(uv2), u1) == 0); + + case UC_ARRAY: + u1 = ucv_array_length(uv1); + u2 = ucv_array_length(uv2); + + if (u1 != u2) + return false; + + for (u1 = 0; u1 < u2; u1++) + if (!ucv_equal(ucv_array_get(uv1, u1), ucv_array_get(uv2, u1))) + return false; + + return true; + + case UC_OBJECT: + u1 = ucv_object_length(uv1); + u2 = ucv_object_length(uv2); + + if (u1 != u2) + return false; + + ucv_object_foreach(uv1, key, val) { + if (!ucv_equal(val, ucv_object_get(uv2, key, NULL))) + return false; + } + + ucv_object_foreach(uv2, key2, val2) { + ucv_object_get(uv1, key2, &b1); + + if (!b1) + return false; + } + + return true; + + default: + return false; + } +} + +void +ucv_gc(uc_vm *vm, bool final) +{ + uc_weakref_t *ref, *tmp; + uc_value_t *val; + size_t i; + + if (!final) { + /* mark reachable objects */ + ucv_gc_mark(vm->globals); + + for (i = 0; i < vm->callframes.count; i++) + ucv_gc_mark(vm->callframes.entries[i].ctx); + + for (i = 0; i < vm->stack.count; i++) + ucv_gc_mark(vm->stack.entries[i]); + } + + /* unref unreachable objects */ + for (ref = vm->values.next; ref != &vm->values; ref = ref->next) { + val = (void *)ref - offsetof(uc_array_t, ref); + + if (ucv_is_marked(val)) + ucv_clear_mark(val); + else + ucv_free(val, true); + } + + /* free destroyed objects */ + for (ref = vm->values.next, tmp = ref->next; ref != &vm->values; ref = tmp, tmp = tmp->next) { + val = (void *)ref - offsetof(uc_array_t, ref); + + if (val->type == UC_NULL) { + ucv_unref(ref); + free(val); + } + } +} + + +#ifdef __GNUC__ + +__attribute__((destructor)) +static void ucv_ressource_types_free(void) +{ + size_t i; + + for (i = 0; i < res_types.count; i++) + ucv_put(res_types.entries[i].proto); + + uc_vector_clear(&res_types); +} + +#endif diff --git a/types.h b/types.h new file mode 100644 index 0000000..a8962da --- /dev/null +++ b/types.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2021 Jo-Philipp Wich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __TYPES_H_ +#define __TYPES_H_ + +#include +#include +#include +#include + +#include "source.h" +#include "chunk.h" + +typedef enum uc_type_t { + UC_NULL, + UC_INTEGER, + UC_BOOLEAN, + UC_STRING, + UC_DOUBLE, + UC_ARRAY, + UC_OBJECT, + UC_REGEXP, + UC_FUNCTION, + UC_CFUNCTION, + UC_CLOSURE, + UC_UPVALUE, + UC_RESSOURCE +} uc_type_t; + +typedef struct uc_value_t { + uint32_t type:4; + uint32_t mark:1; + uint32_t u64:1; + uint32_t refcount:26; +} uc_value_t; + +typedef struct uc_weakref_t { + struct uc_weakref_t *prev; + struct uc_weakref_t *next; +} uc_weakref_t; + +typedef struct { + uc_value_t header; + double dbl; +} uc_double_t; + +typedef struct { + uc_value_t header; + union { + int64_t s64; + uint64_t u64; + } i; +} uc_integer_t; + +typedef struct { + uc_value_t header; + size_t length; + char str[]; +} uc_string_t; + +typedef struct { + uc_value_t header; + uc_weakref_t ref; + size_t count; + uc_value_t *proto; + uc_value_t **entries; +} uc_array_t; + +typedef struct { + uc_value_t header; + uc_weakref_t ref; + uc_value_t *proto; + struct lh_table *table; +} uc_object_t; + +typedef struct { + uc_value_t header; + regex_t regexp; + bool icase, newline, global; + char source[]; +} uc_regexp_t; + +typedef struct { + uc_value_t header; + bool arrow, vararg; + size_t nargs; + size_t nupvals; + size_t srcpos; + uc_chunk chunk; + uc_source *source; + char name[]; +} uc_function_t; + +typedef struct uc_upvalref_t { + uc_value_t header; + size_t slot; + bool closed; + uc_value_t *value; + struct uc_upvalref_t *next; +} uc_upvalref_t; + +typedef struct { + uc_value_t header; + uc_weakref_t ref; + bool is_arrow; + uc_function_t *function; + uc_upvalref_t **upvals; +} uc_closure_t; + +typedef struct uc_vm uc_vm; +typedef uc_value_t *(*uc_cfn_ptr_t)(uc_vm *, size_t); + +typedef struct { + uc_value_t header; + uc_cfn_ptr_t cfn; + char name[]; +} uc_cfunction_t; + +typedef struct { + uc_value_t header; + size_t type; + void *data; +} uc_ressource_t; + +typedef struct { + const char *name; + uc_value_t *proto; + void (*free)(void *); +} uc_ressource_type_t; + +uc_declare_vector(uc_ressource_types_t, uc_ressource_type_t); + +typedef struct printbuf uc_stringbuf_t; + +void ucv_free(uc_value_t *, bool); +void ucv_put(uc_value_t *); + +uc_value_t *ucv_get(uc_value_t *uv); + +uc_type_t ucv_type(uc_value_t *); +const char *ucv_typename(uc_value_t *); + +uc_value_t *ucv_boolean_new(bool); +bool ucv_boolean_get(uc_value_t *); + +uc_value_t *ucv_string_new(const char *); +uc_value_t *ucv_string_new_length(const char *, size_t); +size_t ucv_string_length(uc_value_t *); + +char *_ucv_string_get(uc_value_t **); +#define ucv_string_get(uv) ({ uc_value_t * volatile p = (uv); _ucv_string_get((uc_value_t **)&p); }) + +uc_stringbuf_t *ucv_stringbuf_new(void); +uc_value_t *ucv_stringbuf_finish(uc_stringbuf_t *); + +void _ucv_stringbuf_append(uc_stringbuf_t *, const char *, size_t); + +#define _ucv_is_literal(str) ("" str) +#define ucv_stringbuf_append(buf, str) _ucv_stringbuf_append(buf, _ucv_is_literal(str), sizeof(str) - 1) +#define ucv_stringbuf_addstr(buf, str, len) _ucv_stringbuf_append(buf, str, len) +#define ucv_stringbuf_printf(buf, fmt, ...) sprintbuf(buf, fmt, __VA_ARGS__) + +uc_value_t *ucv_int64_new(int64_t); +uc_value_t *ucv_uint64_new(uint64_t); +int64_t ucv_int64_get(uc_value_t *); +uint64_t ucv_uint64_get(uc_value_t *); + +uc_value_t *ucv_double_new(double); +double ucv_double_get(uc_value_t *); + +uc_value_t *ucv_array_new(uc_vm *); +uc_value_t *ucv_array_new_length(uc_vm *, size_t); +uc_value_t *ucv_array_get(uc_value_t *, size_t); +uc_value_t *ucv_array_pop(uc_value_t *); +uc_value_t *ucv_array_push(uc_value_t *, uc_value_t *); +uc_value_t *ucv_array_shift(uc_value_t *); +uc_value_t *ucv_array_unshift(uc_value_t *, uc_value_t *); +void ucv_array_sort(uc_value_t *, int (*)(const void *, const void *)); +bool ucv_array_delete(uc_value_t *, size_t, size_t); +bool ucv_array_set(uc_value_t *, size_t, uc_value_t *); +size_t ucv_array_length(uc_value_t *); + +uc_value_t *ucv_object_new(uc_vm *); +uc_value_t *ucv_object_get(uc_value_t *, const char *, bool *); +bool ucv_object_add(uc_value_t *, const char *, uc_value_t *); +bool ucv_object_delete(uc_value_t *, const char *); +size_t ucv_object_length(uc_value_t *); + +#define ucv_object_foreach(obj, key, val) \ + char *key; \ + uc_value_t *val __attribute__((__unused__)); \ + for (struct lh_entry *entry ## key = (ucv_type(obj) == UC_OBJECT) ? ((uc_object_t *)obj)->table->head : NULL, *entry_next ## key = NULL; \ + ({ if (entry ## key) { \ + key = (char *)entry ## key->k; \ + val = (uc_value_t *)entry ## key->v; \ + entry_next ## key = entry ## key->next; \ + } ; entry ## key; }); \ + entry ## key = entry_next ## key) + +uc_value_t *ucv_function_new(const char *, size_t, uc_source *); +size_t ucv_function_srcpos(uc_value_t *, size_t); + +uc_value_t *ucv_cfunction_new(const char *, uc_cfn_ptr_t); + +uc_value_t *ucv_closure_new(uc_vm *, uc_function_t *, bool); + +uc_ressource_type_t *ucv_ressource_type_add(const char *, uc_value_t *, void (*)(void *)); +uc_ressource_type_t *ucv_ressource_type_lookup(const char *); + +uc_value_t *ucv_ressource_new(uc_ressource_type_t *, void *); +void **ucv_ressource_dataptr(uc_value_t *, const char *); + +uc_value_t *ucv_regexp_new(const char *, bool, bool, bool, char **); + +uc_value_t *ucv_upvalref_new(size_t); + +uc_value_t *ucv_prototype_get(uc_value_t *); +bool ucv_prototype_set(uc_value_t *, uc_value_t *); + +uc_value_t *ucv_property_get(uc_value_t *, const char *); + +uc_value_t *ucv_from_json(uc_vm *, json_object *); +json_object *ucv_to_json(uc_value_t *); + +char *ucv_to_string(uc_vm *, uc_value_t *); +char *ucv_to_jsonstring(uc_vm *, uc_value_t *); +void ucv_to_stringbuf(uc_vm *, uc_stringbuf_t *, uc_value_t *, bool); + +static inline bool +ucv_is_callable(uc_value_t *uv) +{ + switch (ucv_type(uv)) { + case UC_CLOSURE: + case UC_CFUNCTION: + return true; + + default: + return false; + } +} + +static inline bool +ucv_is_arrowfn(uc_value_t *uv) +{ + uc_closure_t *closure = (uc_closure_t *)uv; + + return (ucv_type(uv) == UC_CLOSURE && closure->is_arrow); +} + +static inline bool +ucv_is_u64(uc_value_t *uv) +{ + return (((uintptr_t)uv & 3) == 0 && uv != NULL && uv->u64 == true); +} + +static inline bool +ucv_is_scalar(uc_value_t *uv) +{ + switch (ucv_type(uv)) { + case UC_NULL: + case UC_BOOLEAN: + case UC_DOUBLE: + case UC_INTEGER: + case UC_STRING: + return true; + + default: + return false; + } +} + +static inline bool +ucv_is_marked(uc_value_t *uv) +{ + return (((uintptr_t)uv & 3) == 0 && uv != NULL && uv->mark == true); +} + +static inline void +ucv_set_mark(uc_value_t *uv) +{ + if (((uintptr_t)uv & 3) == 0 && uv != NULL) + uv->mark = true; +} + +static inline void +ucv_clear_mark(uc_value_t *uv) +{ + if (((uintptr_t)uv & 3) == 0 && uv != NULL) + uv->mark = false; +} + +bool ucv_equal(uc_value_t *, uc_value_t *); + +void ucv_gc(uc_vm *, bool); + +#endif /* __TYPES_H_ */ diff --git a/util.h b/util.h index b7029cc..a62ccc4 100644 --- a/util.h +++ b/util.h @@ -245,4 +245,15 @@ static inline int xvasprintf(char **strp, const char *fmt, va_list ap) { return len; } +static inline struct printbuf *xprintbuf_new(void) { + struct printbuf *pb = printbuf_new(); + + if (!pb) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + return pb; +} + #endif /* __UTIL_H_ */ diff --git a/value.c b/value.c index 4312135..5fafefa 100644 --- a/value.c +++ b/value.c @@ -23,8 +23,8 @@ #include "util.h" #include "chunk.h" #include "value.h" -#include "object.h" #include "lexer.h" /* TK_* */ +#include "vm.h" #define TAG_TYPE uint64_t #define TAG_BITS 3 @@ -44,70 +44,39 @@ #define UC_VALLIST_CHUNK_SIZE 8 -static int -uc_double_tostring(json_object *v, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - double d = json_object_get_double(v); - - if (isnan(d)) - return sprintbuf(pb, strict ? "\"NaN\"" : "NaN"); - - if (d == INFINITY) - return sprintbuf(pb, strict ? "1e309" : "Infinity"); - - if (d == -INFINITY) - return sprintbuf(pb, strict ? "-1e309" : "-Infinity"); - - return sprintbuf(pb, "%g", d); -} - -json_object * -uc_double_new(double v) -{ - json_object *d = json_object_new_double(v); - - if (!d) { - fprintf(stderr, "Out of memory\n"); - abort(); - } - - json_object_set_serializer(d, uc_double_tostring, NULL, NULL); - - return d; -} - bool -uc_val_is_truish(json_object *val) +uc_val_is_truish(uc_value_t *val) { double d; - switch (json_object_get_type(val)) { - case json_type_int: - return (json_object_get_int64(val) != 0); + switch (ucv_type(val)) { + case UC_INTEGER: + if (ucv_is_u64(val)) + return (ucv_uint64_get(val) != 0); + + return (ucv_int64_get(val) != 0); - case json_type_double: - d = json_object_get_double(val); + case UC_DOUBLE: + d = ucv_double_get(val); return (d != 0 && !isnan(d)); - case json_type_boolean: - return (json_object_get_boolean(val) != false); + case UC_BOOLEAN: + return ucv_boolean_get(val); - case json_type_string: - return (json_object_get_string_len(val) > 0); + case UC_STRING: + return (ucv_string_length(val) > 0); - case json_type_array: - case json_type_object: - return true; + case UC_NULL: + return false; default: - return false; + return true; } } -enum json_type -uc_cast_number(json_object *v, int64_t *n, double *d) +uc_type_t +uc_cast_number(uc_value_t *v, int64_t *n, double *d) { bool is_double = false; const char *s; @@ -116,27 +85,27 @@ uc_cast_number(json_object *v, int64_t *n, double *d) *d = 0.0; *n = 0; - switch (json_object_get_type(v)) { - case json_type_int: - *n = json_object_get_int64(v); + switch (ucv_type(v)) { + case UC_INTEGER: + *n = ucv_int64_get(v); - return json_type_int; + return UC_INTEGER; - case json_type_double: - *d = json_object_get_double(v); + case UC_DOUBLE: + *d = ucv_double_get(v); - return json_type_double; + return UC_DOUBLE; - case json_type_null: - return json_type_int; + case UC_NULL: + return UC_INTEGER; - case json_type_boolean: - *n = json_object_get_boolean(v) ? 1 : 0; + case UC_BOOLEAN: + *n = ucv_boolean_get(v); - return json_type_int; + return UC_INTEGER; - case json_type_string: - s = json_object_get_string(v); + case UC_STRING: + s = ucv_string_get(v); while (isspace(*s)) s++; @@ -162,141 +131,144 @@ uc_cast_number(json_object *v, int64_t *n, double *d) if (*e) { *d = NAN; - return json_type_double; + return UC_DOUBLE; } if (is_double) - return json_type_double; + return UC_DOUBLE; - return json_type_int; + return UC_INTEGER; default: *d = NAN; - return json_type_double; + return UC_DOUBLE; } } -static json_object * -uc_getproto(json_object *obj) +static char * +uc_tostring(uc_vm *vm, uc_value_t *val) { - uc_prototype *proto; - - switch (uc_object_type(obj)) { - case UC_OBJ_RESSOURCE: - proto = uc_ressource_prototype(obj); - break; - - case UC_OBJ_PROTOTYPE: - proto = uc_object_as_prototype(obj)->parent; - break; - - default: - proto = NULL; - } + if (ucv_type(val) != UC_STRING) + return ucv_to_string(vm, val); - return proto ? proto->header.jso : NULL; + return NULL; } -json_object * -uc_getval(json_object *scope, json_object *key) +static int64_t +uc_toidx(uc_value_t *val) { - json_object *o, *v; const char *k; int64_t idx; double d; char *e; - if (json_object_is_type(scope, json_type_array)) { - /* only consider doubles with integer values as array keys */ - if (json_object_is_type(key, json_type_double)) { - d = json_object_get_double(key); + /* only consider doubles with integer values as array keys */ + if (ucv_type(val) == UC_DOUBLE) { + d = ucv_double_get(val); - if ((double)(int64_t)(d) == d) - idx = (int64_t)d; - else - idx = -1; - } - else if (json_object_is_type(key, json_type_int)) { - idx = json_object_get_int64(key); - } - else if (json_object_is_type(key, json_type_string)) { - errno = 0; - k = json_object_get_string(key); - idx = strtoll(k, &e, 0); + if ((double)(int64_t)(d) != d) + return -1; - if (errno != 0 || e == k || *e != 0) - idx = -1; - } - else { - idx = -1; - } + return (int64_t)d; + } + else if (ucv_type(val) == UC_INTEGER) { + return ucv_int64_get(val); + } + else if (ucv_type(val) == UC_STRING) { + errno = 0; + k = ucv_string_get(val); + idx = strtoll(k, &e, 0); - if (idx >= 0 && idx < json_object_array_length(scope)) - return json_object_get(json_object_array_get_idx(scope, idx)); + if (errno != 0 || e == k || *e != 0) + return -1; + + return idx; + } + + return -1; +} + +uc_value_t * +uc_getval(uc_vm *vm, uc_value_t *scope, uc_value_t *key) +{ + uc_value_t *o, *v = NULL; + int64_t idx; + bool found; + char *k; + + if (ucv_type(scope) == UC_ARRAY) { + idx = uc_toidx(key); + + if (idx >= 0 && (uint64_t)idx < ucv_array_length(scope)) + return ucv_get(ucv_array_get(scope, idx)); } - for (o = scope, k = key ? json_object_get_string(key) : "null"; o; o = uc_getproto(o)) { - if (!json_object_is_type(o, json_type_object)) + k = uc_tostring(vm, key); + + for (o = scope; o; o = ucv_prototype_get(o)) { + if (ucv_type(o) != UC_OBJECT) continue; - if (json_object_object_get_ex(o, k, &v)) - return json_object_get(v); + v = ucv_object_get(o, k ? k : ucv_string_get(key), &found); + + if (found) + break; } - return NULL; + free(k); + + return ucv_get(v); } -json_object * -uc_setval(json_object *scope, json_object *key, json_object *val) +uc_value_t * +uc_setval(uc_vm *vm, uc_value_t *scope, uc_value_t *key, uc_value_t *val) { int64_t idx; + char *s; + bool rv; if (!key) return NULL; - if (json_object_is_type(scope, json_type_array)) { - errno = 0; - idx = json_object_get_int64(key); - - if (errno != 0) - return NULL; + if (ucv_type(scope) == UC_ARRAY) { + idx = uc_toidx(key); - if (json_object_array_put_idx(scope, idx, val)) + if (idx < 0 || !ucv_array_set(scope, idx, val)) return NULL; - return json_object_get(val); + return ucv_get(val); } - if (json_object_object_add(scope, key ? json_object_get_string(key) : "null", val)) - return NULL; + s = uc_tostring(vm, key); + rv = ucv_object_add(scope, s ? s : ucv_string_get(key), val); + free(s); - return json_object_get(val); + return rv ? ucv_get(val) : NULL; } bool -uc_cmp(int how, json_object *v1, json_object *v2) +uc_cmp(int how, uc_value_t *v1, uc_value_t *v2) { - enum json_type t1 = json_object_get_type(v1); - enum json_type t2 = json_object_get_type(v2); + uc_type_t t1 = ucv_type(v1); + uc_type_t t2 = ucv_type(v2); int64_t n1, n2, delta; double d1, d2; - if (t1 == json_type_string && t2 == json_type_string) { - delta = strcmp(json_object_get_string(v1), json_object_get_string(v2)); + if (t1 == UC_STRING && t2 == UC_STRING) { + delta = strcmp(ucv_string_get(v1), ucv_string_get(v2)); } else { - if ((t1 == json_type_array && t2 == json_type_array) || - (t1 == json_type_object && t2 == json_type_object)) { + if (t1 == t2 && !ucv_is_scalar(v1)) { delta = (void *)v1 - (void *)v2; } else { t1 = uc_cast_number(v1, &n1, &d1); t2 = uc_cast_number(v2, &n2, &d2); - if (t1 == json_type_double || t2 == json_type_double) { - d1 = (t1 == json_type_double) ? d1 : (double)n1; - d2 = (t2 == json_type_double) ? d2 : (double)n2; + if (t1 == UC_DOUBLE || t2 == UC_DOUBLE) { + d1 = (t1 == UC_DOUBLE) ? d1 : (double)n1; + d2 = (t2 == UC_DOUBLE) ? d2 : (double)n2; /* all comparison results except `!=` involving NaN are false */ if (isnan(d1) || isnan(d2)) @@ -339,44 +311,6 @@ uc_cmp(int how, json_object *v1, json_object *v2) } } -bool -uc_eq(json_object *v1, json_object *v2) -{ - uc_objtype_t o1 = uc_object_type(v1); - uc_objtype_t o2 = uc_object_type(v2); - enum json_type t1 = json_object_get_type(v1); - enum json_type t2 = json_object_get_type(v2); - - if (o1 != o2 || t1 != t2) - return false; - - switch (t1) { - case json_type_array: - case json_type_object: - return (v1 == v2); - - case json_type_boolean: - return (json_object_get_boolean(v1) == json_object_get_boolean(v2)); - - case json_type_double: - if (isnan(json_object_get_double(v1)) || isnan(json_object_get_double(v2))) - return false; - - return (json_object_get_double(v1) == json_object_get_double(v2)); - - case json_type_int: - return (json_object_get_int64(v1) == json_object_get_int64(v2)); - - case json_type_string: - return !strcmp(json_object_get_string(v1), json_object_get_string(v2)); - - case json_type_null: - return true; - } - - return false; -} - void uc_vallist_init(uc_value_list *list) { @@ -389,14 +323,14 @@ uc_vallist_init(uc_value_list *list) void uc_vallist_free(uc_value_list *list) { - json_object *o; + uc_value_t *o; size_t i; for (i = 0; i < list->isize; i++) { if (TAG_GET_TYPE(list->index[i]) == TAG_PTR) { o = uc_vallist_get(list, i); - uc_value_put(o); - uc_value_put(o); + ucv_put(o); + ucv_put(o); } } @@ -507,7 +441,7 @@ add_str(uc_value_list *list, const char *s, size_t slen) uint32_t sl; size_t sz; char *dst; - int i; + size_t i; if (slen > UINT32_MAX) { fprintf(stderr, "String constant too long\n"); @@ -609,7 +543,7 @@ add_ptr(uc_value_list *list, void *ptr) } ssize_t -uc_vallist_add(uc_value_list *list, json_object *value) +uc_vallist_add(uc_value_list *list, uc_value_t *value) { ssize_t existing; @@ -618,42 +552,43 @@ uc_vallist_add(uc_value_list *list, json_object *value) memset(&list->index[list->isize], 0, UC_VALLIST_CHUNK_SIZE); } - switch (json_object_get_type(value)) { - case json_type_int: - existing = find_num(list, json_object_get_int64(value)); + switch (ucv_type(value)) { + case UC_INTEGER: + /* XXX: u64 */ + existing = find_num(list, ucv_int64_get(value)); if (existing > -1) return existing; - add_num(list, json_object_get_int64(value)); + add_num(list, ucv_int64_get(value)); break; - case json_type_double: - existing = find_dbl(list, json_object_get_double(value)); + case UC_DOUBLE: + existing = find_dbl(list, ucv_double_get(value)); if (existing > -1) return existing; - add_dbl(list, json_object_get_double(value)); + add_dbl(list, ucv_double_get(value)); break; - case json_type_string: + case UC_STRING: existing = find_str(list, - json_object_get_string(value), - json_object_get_string_len(value)); + ucv_string_get(value), + ucv_string_length(value)); if (existing > -1) return existing; add_str(list, - json_object_get_string(value), - json_object_get_string_len(value)); + ucv_string_get(value), + ucv_string_length(value)); break; - case json_type_object: + case UC_FUNCTION: add_ptr(list, value); break; @@ -673,28 +608,28 @@ uc_vallist_type(uc_value_list *list, size_t idx) return TAG_GET_TYPE(list->index[idx]); } -json_object * +uc_value_t * uc_vallist_get(uc_value_list *list, size_t idx) { char str[sizeof(TAG_TYPE)]; - size_t len; - int n; + size_t n, len; switch (uc_vallist_type(list, idx)) { case TAG_NUM: - return xjs_new_int64(TAG_GET_NV(list->index[idx])); + return ucv_int64_new(TAG_GET_NV(list->index[idx])); case TAG_LNUM: if (TAG_GET_OFFSET(list->index[idx]) + sizeof(int64_t) > list->dsize) return NULL; - return xjs_new_int64(be64toh(*(int64_t *)(list->data + TAG_GET_OFFSET(list->index[idx])))); + /* XXX: u64 */ + return ucv_int64_new(be64toh(*(int64_t *)(list->data + TAG_GET_OFFSET(list->index[idx])))); case TAG_DBL: if (TAG_GET_OFFSET(list->index[idx]) + sizeof(double) > list->dsize) return NULL; - return uc_double_new(*(double *)(list->data + TAG_GET_OFFSET(list->index[idx]))); + return ucv_double_new(*(double *)(list->data + TAG_GET_OFFSET(list->index[idx]))); case TAG_STR: len = TAG_GET_STR_L(list->index[idx]); @@ -702,7 +637,7 @@ uc_vallist_get(uc_value_list *list, size_t idx) for (n = 0; n < len; n++) str[n] = (list->index[idx] >> ((n + 1) << 3)); - return xjs_new_string_len(str, len); + return ucv_string_new_length(str, len); case TAG_LSTR: if (TAG_GET_OFFSET(list->index[idx]) + sizeof(uint32_t) > list->dsize) @@ -713,13 +648,13 @@ uc_vallist_get(uc_value_list *list, size_t idx) if (TAG_GET_OFFSET(list->index[idx]) + sizeof(uint32_t) + len > list->dsize) return NULL; - return xjs_new_string_len(list->data + TAG_GET_OFFSET(list->index[idx]) + sizeof(uint32_t), len); + return ucv_string_new_length(list->data + TAG_GET_OFFSET(list->index[idx]) + sizeof(uint32_t), len); case TAG_PTR: if (TAG_GET_OFFSET(list->index[idx]) + sizeof(void *) > list->dsize) return NULL; - return uc_value_get(*(json_object **)(list->data + TAG_GET_OFFSET(list->index[idx]))); + return ucv_get(*(uc_value_t **)(list->data + TAG_GET_OFFSET(list->index[idx]))); default: return NULL; diff --git a/value.h b/value.h index e9d5ce6..770279e 100644 --- a/value.h +++ b/value.h @@ -46,36 +46,25 @@ typedef struct { char *data; } uc_value_list; -json_object *uc_double_new(double v); +typedef struct uc_value_t uc_value_t; -bool uc_eq(json_object *v1, json_object *v2); -bool uc_cmp(int how, json_object *v1, json_object *v2); -bool uc_val_is_truish(json_object *val); +bool uc_cmp(int how, uc_value_t *v1, uc_value_t *v2); +bool uc_val_is_truish(uc_value_t *val); -enum json_type uc_cast_number(json_object *v, int64_t *n, double *d); +typedef enum uc_type_t uc_type_t; +typedef struct uc_value_t uc_value_t; +uc_type_t uc_cast_number(uc_value_t *v, int64_t *n, double *d); -json_object *uc_getval(json_object *scope, json_object *key); -json_object *uc_setval(json_object *scope, json_object *key, json_object *val); +typedef struct uc_vm uc_vm; + +uc_value_t *uc_getval(uc_vm *, uc_value_t *scope, uc_value_t *key); +uc_value_t *uc_setval(uc_vm *, uc_value_t *scope, uc_value_t *key, uc_value_t *val); void uc_vallist_init(uc_value_list *list); void uc_vallist_free(uc_value_list *list); -ssize_t uc_vallist_add(uc_value_list *list, json_object *value); +ssize_t uc_vallist_add(uc_value_list *list, uc_value_t *value); uc_value_type_t uc_vallist_type(uc_value_list *list, size_t idx); -struct json_object *uc_vallist_get(uc_value_list *list, size_t idx); - -#define uc_value_get(val) \ - ({ \ - struct json_object *__o = val; \ - /*fprintf(stderr, "get(%p // %s) [%d + 1] @ %s:%d\n", __o, json_object_to_json_string(__o), getrefcnt(__o), __FILE__, __LINE__);*/ \ - json_object_get(__o); \ - }) - -#define uc_value_put(val) \ - ({ \ - struct json_object *__o = val; \ - /*fprintf(stderr, "put(%p // %s) [%d - 1] @ %s:%d\n", __o, json_object_to_json_string(__o), getrefcnt(__o), __FILE__, __LINE__);*/ \ - json_object_put(__o); \ - }) +uc_value_t *uc_vallist_get(uc_value_list *list, size_t idx); #endif /* __VALUE_H_ */ diff --git a/vm.c b/vm.c index ce1aa0c..10e3286 100644 --- a/vm.c +++ b/vm.c @@ -51,8 +51,8 @@ static const uc_insn_definition insn_defs[__I_MAX] = { [I_LUPV] = { 0, 1, 4 }, [I_LVAL] = { 2, 1, 0 }, - [I_CLFN] = { 0, 1, 4, true }, - [I_ARFN] = { 0, 1, 4, true }, + [I_CLFN] = { 0, 1, 4 }, + [I_ARFN] = { 0, 1, 4 }, [I_SLOC] = { 0, 0, 4 }, [I_SUPV] = { 0, 0, 4 }, @@ -90,8 +90,8 @@ static const uc_insn_definition insn_defs[__I_MAX] = { [I_GT] = { 2, 1, 0 }, [I_IN] = { 2, 1, 0 }, - [I_JMP] = { 0, 0, -4, true }, - [I_JMPZ] = { 1, 0, -4, true }, + [I_JMP] = { 0, 0, -4 }, + [I_JMPZ] = { 1, 0, -4 }, [I_COPY] = { 0, 1, 1 }, [I_POP] = { 1, 0, 0 }, @@ -124,19 +124,19 @@ uc_vm_reset_stack(uc_vm *vm) { while (vm->stack.count > 0) { vm->stack.count--; - uc_value_put(vm->stack.entries[vm->stack.count]); + ucv_put(vm->stack.entries[vm->stack.count]); vm->stack.entries[vm->stack.count] = NULL; } } -static json_object * +static uc_value_t * uc_vm_callframe_pop(uc_vm *vm); static void uc_vm_reset_callframes(uc_vm *vm) { while (vm->callframes.count > 0) - uc_value_put(uc_vm_callframe_pop(vm)); + ucv_put(uc_vm_callframe_pop(vm)); } void uc_vm_init(uc_vm *vm, uc_parse_config *config) @@ -152,19 +152,22 @@ void uc_vm_init(uc_vm *vm, uc_parse_config *config) vm->open_upvals = NULL; + vm->values.prev = &vm->values; + vm->values.next = &vm->values; + uc_vm_reset_stack(vm); } void uc_vm_free(uc_vm *vm) { - uc_upvalref *ref; + uc_upvalref_t *ref; - uc_value_put(vm->exception.stacktrace); + ucv_put(vm->exception.stacktrace); free(vm->exception.message); while (vm->open_upvals) { ref = vm->open_upvals->next; - uc_value_put(vm->open_upvals->header.jso); + ucv_put(&vm->open_upvals->header); vm->open_upvals = ref; } @@ -172,6 +175,8 @@ void uc_vm_free(uc_vm *vm) uc_vm_reset_stack(vm); uc_vector_clear(&vm->stack); uc_vector_clear(&vm->callframes); + + ucv_gc(vm, true); } static uc_chunk * @@ -263,11 +268,12 @@ static void uc_vm_frame_dump(uc_vm *vm, uc_callframe *frame) { uc_chunk *chunk = uc_vm_frame_chunk(frame); - uc_function *function; - uc_closure *closure; - uc_upvalref *ref; - json_object *v; + uc_function_t *function; + uc_closure_t *closure; + uc_upvalref_t *ref; + uc_value_t *v; size_t i; + char *s; fprintf(stderr, " [*] CALLFRAME[%zx]\n", frame - vm->callframes.entries); @@ -275,8 +281,9 @@ uc_vm_frame_dump(uc_vm *vm, uc_callframe *frame) fprintf(stderr, " |- stackframe %zu/%zu\n", frame->stackframe, vm->stack.count); - fprintf(stderr, " |- ctx %s\n", - json_object_to_json_string(frame->ctx)); + s = ucv_to_string(NULL, frame->ctx); + fprintf(stderr, " |- ctx %s\n", s); + free(s); if (chunk) { fprintf(stderr, " |- %zu constants\n", @@ -284,12 +291,10 @@ uc_vm_frame_dump(uc_vm *vm, uc_callframe *frame) for (i = 0; i < chunk->constants.isize; i++) { v = uc_chunk_get_constant(chunk, i); - - fprintf(stderr, " | [%zu] %s\n", - i, - json_object_to_json_string(v)); - - uc_value_put(v); + s = ucv_to_jsonstring(NULL, v); + fprintf(stderr, " | [%zu] %s\n", i, s); + free(s); + ucv_put(v); } closure = frame->closure; @@ -301,80 +306,88 @@ uc_vm_frame_dump(uc_vm *vm, uc_callframe *frame) for (i = 0; i < function->nupvals; i++) { ref = closure->upvals[i]; v = uc_chunk_debug_get_variable(chunk, 0, i, true); + s = ucv_to_string(NULL, v); + fprintf(stderr, " [%zu] <%p> %s ", i, ref, s); + free(s); - if (ref->closed) - fprintf(stderr, " [%zu] <%p> %s {closed} %s\n", - i, - ref, - json_object_to_json_string(v), - json_object_to_json_string(ref->value)); - else - fprintf(stderr, " [%zu] <%p> %s {open[%zu]} %s\n", - i, - ref, - json_object_to_json_string(v), - ref->slot, - json_object_to_json_string(vm->stack.entries[ref->slot])); - - uc_value_put(v); + if (ref->closed) { + s = ucv_to_jsonstring(NULL, ref->value); + fprintf(stderr, "{closed} %s\n", s); + } + else { + s = ucv_to_jsonstring(NULL, vm->stack.entries[ref->slot]); + fprintf(stderr, "{open[%zu]} %s\n", ref->slot, s); + } + + ucv_put(v); + free(s); } } } void -uc_vm_stack_push(uc_vm *vm, json_object *value) +uc_vm_stack_push(uc_vm *vm, uc_value_t *value) { + char *s; + uc_vector_grow(&vm->stack); - uc_value_put(vm->stack.entries[vm->stack.count]); + ucv_put(vm->stack.entries[vm->stack.count]); vm->stack.entries[vm->stack.count] = value; vm->stack.count++; - if (vm->trace) - fprintf(stderr, " [+%zd] %s\n", - vm->stack.count - 1, - json_object_to_json_string(value)); + if (vm->trace) { + s = ucv_to_jsonstring(NULL, value); + fprintf(stderr, " [+%zd] %s\n", vm->stack.count - 1, s); + free(s); + } } -json_object * +uc_value_t * uc_vm_stack_pop(uc_vm *vm) { - json_object *rv; + uc_value_t *rv; + char *s; vm->stack.count--; rv = vm->stack.entries[vm->stack.count]; vm->stack.entries[vm->stack.count] = NULL; - if (vm->trace) - fprintf(stderr, " [-%zd] %s\n", - vm->stack.count, - json_object_to_json_string(rv)); + if (vm->trace) { + s = ucv_to_jsonstring(NULL, rv); + fprintf(stderr, " [-%zd] %s\n", vm->stack.count, s); + free(s); + } return rv; } -json_object * +uc_value_t * uc_vm_stack_peek(uc_vm *vm, size_t offset) { return vm->stack.entries[vm->stack.count + (-1 - offset)]; } static void -uc_vm_stack_set(uc_vm *vm, size_t offset, json_object *value) +uc_vm_stack_set(uc_vm *vm, size_t offset, uc_value_t *value) { - if (vm->trace) - fprintf(stderr, " [!%zu] %s\n", - offset, json_object_to_json_string(value)); + char *s; - uc_value_put(vm->stack.entries[offset]); + if (vm->trace) { + s = ucv_to_jsonstring(NULL, value); + fprintf(stderr, " [!%zu] %s\n", offset, s); + free(s); + } + + ucv_put(vm->stack.entries[offset]); vm->stack.entries[offset] = value; } static void -uc_vm_call_native(uc_vm *vm, json_object *ctx, uc_cfunction *fptr, bool mcall, size_t nargs) +uc_vm_call_native(uc_vm *vm, uc_value_t *ctx, uc_cfunction_t *fptr, bool mcall, size_t nargs) { - json_object *res = NULL; + uc_value_t *res = NULL; uc_callframe *frame; /* add new callframe */ @@ -393,24 +406,25 @@ uc_vm_call_native(uc_vm *vm, json_object *ctx, uc_cfunction *fptr, bool mcall, s res = fptr->cfn(vm, nargs); /* reset stack */ - uc_value_put(uc_vm_callframe_pop(vm)); + ucv_put(uc_vm_callframe_pop(vm)); /* push return value */ if (!vm->exception.type) uc_vm_stack_push(vm, res); else - uc_value_put(res); + ucv_put(res); } static bool -uc_vm_call_function(uc_vm *vm, json_object *ctx, json_object *fno, bool mcall, size_t argspec) +uc_vm_call_function(uc_vm *vm, uc_value_t *ctx, uc_value_t *fno, bool mcall, size_t argspec) { size_t i, j, stackoff, nargs = argspec & 0xffff, nspreads = argspec >> 16; uc_callframe *frame = uc_vm_current_frame(vm); - json_object *ellip, *arg; - uc_function *function; - uc_closure *closure; + uc_value_t *ellip, *arg; + uc_function_t *function; + uc_closure_t *closure; uint16_t slot, tmp; + char *s; /* XXX: make dependent on stack size */ if (vm->callframes.count >= 1000) { @@ -424,11 +438,11 @@ uc_vm_call_function(uc_vm *vm, json_object *ctx, json_object *fno, bool mcall, s /* argument list contains spread operations, we need to reshuffle the stack */ if (nspreads > 0) { /* create temporary array */ - ellip = xjs_new_array_size(nargs); + ellip = ucv_array_new_length(vm, nargs); /* pop original stack values and push to temp array in reverse order */ for (i = 0; i < nargs; i++) - json_object_array_add(ellip, uc_vm_stack_pop(vm)); + ucv_array_push(ellip, uc_vm_stack_pop(vm)); /* for each spread value index ... */ for (i = 0, slot = nargs; i < nspreads; i++) { @@ -437,61 +451,61 @@ uc_vm_call_function(uc_vm *vm, json_object *ctx, json_object *fno, bool mcall, s frame->ip += 2; /* push each preceeding non-spread value to the stack */ - for (j = slot; j > tmp + 1; j--) - uc_vm_stack_push(vm, uc_value_get(json_object_array_get_idx(ellip, j - 1))); + for (j = slot; j > tmp + 1UL; j--) + uc_vm_stack_push(vm, ucv_get(ucv_array_get(ellip, j - 1))); /* read spread value at index... */ slot = tmp; - arg = uc_value_get(json_object_array_get_idx(ellip, slot)); + arg = ucv_get(ucv_array_get(ellip, slot)); /* ... ensure that it is an array type ... */ - if (!json_object_is_type(arg, json_type_array)) { - uc_vm_raise_exception(vm, EXCEPTION_TYPE, - "(%s) is not iterable", - json_object_to_json_string(arg)); + if (ucv_type(arg) != UC_ARRAY) { + s = ucv_to_string(vm, arg); + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "(%s) is not iterable", s); + free(s); return false; } /* ... and push each spread array value as argument to the stack */ - for (j = 0; j < json_object_array_length(arg); j++) - uc_vm_stack_push(vm, uc_value_get(json_object_array_get_idx(arg, j))); + for (j = 0; j < ucv_array_length(arg); j++) + uc_vm_stack_push(vm, ucv_get(ucv_array_get(arg, j))); - uc_value_put(arg); + ucv_put(arg); } /* push remaining non-spread arguments to the stack */ for (i = slot; i > 0; i--) - uc_vm_stack_push(vm, uc_value_get(json_object_array_get_idx(ellip, i - 1))); + uc_vm_stack_push(vm, ucv_get(ucv_array_get(ellip, i - 1))); /* free temp array */ - uc_value_put(ellip); + ucv_put(ellip); /* update arg count */ nargs = vm->stack.count - stackoff - 1; } /* is a native function */ - if (uc_object_is_type(fno, UC_OBJ_CFUNCTION)) { - uc_vm_call_native(vm, ctx, uc_object_as_cfunction(fno), mcall, nargs); + if (ucv_type(fno) == UC_CFUNCTION) { + uc_vm_call_native(vm, ctx, (uc_cfunction_t *)fno, mcall, nargs); return true; } - if (!uc_object_is_type(fno, UC_OBJ_CLOSURE)) { + if (ucv_type(fno) != UC_CLOSURE) { uc_vm_raise_exception(vm, EXCEPTION_TYPE, "left-hand side is not a function"); return false; } - closure = uc_object_as_closure(fno); + closure = (uc_closure_t *)fno; function = closure->function; /* fewer arguments on stack than function expects => pad */ if (nargs < function->nargs) { for (i = nargs; i < function->nargs; i++) { if (function->vararg && (i + 1) == function->nargs) - uc_vm_stack_push(vm, xjs_new_array_size(0)); + uc_vm_stack_push(vm, ucv_array_new_length(vm, 0)); else uc_vm_stack_push(vm, NULL); } @@ -501,10 +515,10 @@ uc_vm_call_function(uc_vm *vm, json_object *ctx, json_object *fno, bool mcall, s else if (nargs > function->nargs - function->vararg) { /* is a vararg function => pass excess args as array */ if (function->vararg) { - ellip = xjs_new_array_size(nargs - (function->nargs - 1)); + ellip = ucv_array_new_length(vm, nargs - (function->nargs - 1)); for (i = function->nargs; i <= nargs; i++) - json_object_array_add(ellip, uc_vm_stack_peek(vm, nargs - i)); + ucv_array_push(ellip, uc_vm_stack_peek(vm, nargs - i)); for (i = function->nargs; i <= nargs; i++) uc_vm_stack_pop(vm); @@ -515,7 +529,7 @@ uc_vm_call_function(uc_vm *vm, json_object *ctx, json_object *fno, bool mcall, s /* static amount of args => drop excess values */ else { for (i = function->nargs; i < nargs; i++) - uc_value_put(uc_vm_stack_pop(vm)); + ucv_put(uc_vm_stack_pop(vm)); } } @@ -543,19 +557,19 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); uc_chunk *chunk = uc_vm_frame_chunk(frame); - size_t msglen = 0, srcpos; - json_object *cnst = NULL; - char *msg = NULL; + uc_stringbuf_t *buf = NULL; + uc_value_t *cnst = NULL; + size_t srcpos; + char *s; - srcpos = uc_function_get_srcpos(frame->closure->function, pos - chunk->entries); + srcpos = ucv_function_srcpos((uc_value_t *)frame->closure->function, pos - chunk->entries); if (last_srcpos == 0 || last_source != frame->closure->function->source || srcpos != last_srcpos) { - format_source_context(&msg, &msglen, - frame->closure->function->source, - srcpos, true); + buf = xprintbuf_new(); - fprintf(stderr, "%s", msg); - free(msg); + format_source_context(buf, frame->closure->function->source, srcpos, true); + fwrite(buf->buf, 1, printbuf_length(buf), stderr); + printbuf_free(buf); last_source = frame->closure->function->source; last_srcpos = srcpos; @@ -574,13 +588,13 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) case -2: fprintf(stderr, " {%c0x%hx}", vm->arg.s16 < 0 ? '-' : '+', - vm->arg.s16 < 0 ? -(unsigned)vm->arg.s16 : vm->arg.s16); + vm->arg.s16 < 0 ? -(unsigned)vm->arg.s16 : (unsigned)vm->arg.s16); break; case -4: fprintf(stderr, " {%c0x%x}", vm->arg.s32 < 0 ? '-' : '+', - vm->arg.s32 < 0 ? -(unsigned)vm->arg.s32 : vm->arg.s32); + vm->arg.s32 < 0 ? -(unsigned)vm->arg.s32 : (unsigned)vm->arg.s32); break; case 1: @@ -605,9 +619,11 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) case I_LVAR: case I_SVAR: cnst = uc_chunk_get_constant(uc_vm_frame_chunk(uc_vector_last(&vm->callframes)), vm->arg.u32); + s = cnst ? ucv_to_jsonstring(NULL, cnst) : NULL; - fprintf(stderr, "\t; %s", cnst ? json_object_to_json_string(cnst) : "null"); - uc_value_put(cnst); + fprintf(stderr, "\t; %s", s ? s : "(?)"); + ucv_put(cnst); + free(s); break; case I_LLOC: @@ -615,9 +631,11 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) case I_SLOC: case I_SUPV: cnst = uc_chunk_debug_get_variable(chunk, pos - chunk->entries, vm->arg.u32, (insn == I_LUPV || insn == I_SUPV)); + s = cnst ? ucv_to_jsonstring(NULL, cnst) : NULL; - fprintf(stderr, "\t; %s", cnst ? json_object_to_json_string(cnst) : "(?)"); - uc_value_put(cnst); + fprintf(stderr, "\t; %s", s ? s : "(?)"); + ucv_put(cnst); + free(s); break; case I_ULOC: @@ -629,11 +647,14 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) if (!cnst) cnst = uc_chunk_get_constant(uc_vm_frame_chunk(uc_vector_last(&vm->callframes)), vm->arg.u32 & 0x00ffffff); + s = cnst ? ucv_to_jsonstring(NULL, cnst) : NULL; + fprintf(stderr, "\t; %s (%s)", - cnst ? json_object_to_json_string(cnst) : "(?)", + s ? s : "(?)", insn_names[vm->arg.u32 >> 24]); - uc_value_put(cnst); + ucv_put(cnst); + free(s); break; case I_UVAL: @@ -647,14 +668,38 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) fprintf(stderr, "\n"); } -static int -uc_vm_exception_tostring(json_object *jso, struct printbuf *pb, int level, int flags) +static uc_value_t * +uc_vm_exception_tostring(uc_vm *vm, size_t nargs) { - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - json_object *message = json_object_object_get(jso, "message"); + uc_callframe *frame = uc_vm_current_frame(vm); + uc_value_t *message = ucv_object_get(frame->ctx, "message", NULL); - return sprintbuf(pb, "%s", - strict ? json_object_to_json_string(message) : json_object_get_string(message)); + return message ? ucv_get(message) : ucv_string_new("Exception"); +} + +static uc_value_t *exception_prototype = NULL; + +static uc_value_t * +uc_vm_exception_new(uc_vm *vm, uc_exception_type_t type, const char *message, uc_value_t *stacktrace) +{ + uc_value_t *exo; + + if (exception_prototype == NULL) { + exception_prototype = ucv_object_new(vm); + + ucv_object_add(exception_prototype, "tostring", + ucv_cfunction_new("tostring", uc_vm_exception_tostring)); + } + + exo = ucv_object_new(vm); + + ucv_object_add(exo, "type", ucv_string_new(exception_type_strings[type])); + ucv_object_add(exo, "message", ucv_string_new(message)); + ucv_object_add(exo, "stacktrace", ucv_get(stacktrace)); + + ucv_prototype_set(exo, ucv_get(exception_prototype)); + + return exo; } static bool @@ -662,7 +707,7 @@ uc_vm_handle_exception(uc_vm *vm) { uc_callframe *frame = uc_vm_current_frame(vm); uc_chunk *chunk = NULL; - json_object *exo; + uc_value_t *exo; size_t i, pos; if (!frame->closure) @@ -680,16 +725,11 @@ uc_vm_handle_exception(uc_vm *vm) /* we found a matching range... first unwind stack */ while (vm->stack.count > frame->stackframe + chunk->ehranges.entries[i].slot) - uc_value_put(uc_vm_stack_pop(vm)); + ucv_put(uc_vm_stack_pop(vm)); /* prepare exception object and expose it to user handler code */ - exo = xjs_new_object(); + exo = uc_vm_exception_new(vm, vm->exception.type, vm->exception.message, vm->exception.stacktrace); - json_object_object_add(exo, "type", xjs_new_string(exception_type_strings[vm->exception.type])); - json_object_object_add(exo, "message", xjs_new_string(vm->exception.message)); - json_object_object_add(exo, "stacktrace", uc_value_get(vm->exception.stacktrace)); - - json_object_set_serializer(exo, uc_vm_exception_tostring, NULL, NULL); uc_vm_stack_push(vm, exo); /* reset exception information */ @@ -721,35 +761,35 @@ uc_vm_handle_exception(uc_vm *vm) return false; } -static json_object * +static uc_value_t * uc_vm_capture_stacktrace(uc_vm *vm, size_t i) { - json_object *stacktrace, *entry, *last = NULL; - uc_function *function; + uc_value_t *stacktrace, *entry, *last = NULL; + uc_function_t *function; uc_callframe *frame; size_t off, srcpos; char *name; - stacktrace = xjs_new_array(); + stacktrace = ucv_array_new(vm); for (; i > 0; i--) { frame = &vm->callframes.entries[i - 1]; - entry = xjs_new_object(); + entry = ucv_object_new(vm); if (frame->closure) { function = frame->closure->function; off = (frame->ip - uc_vm_frame_chunk(frame)->entries) - 1; - srcpos = uc_function_get_srcpos(function, off); + srcpos = ucv_function_srcpos((uc_value_t *)function, off); - json_object_object_add(entry, "filename", xjs_new_string(function->source->filename)); - json_object_object_add(entry, "line", xjs_new_int64(uc_source_get_line(function->source, &srcpos))); - json_object_object_add(entry, "byte", xjs_new_int64(srcpos)); + ucv_object_add(entry, "filename", ucv_string_new(function->source->filename)); + ucv_object_add(entry, "line", ucv_int64_new(uc_source_get_line(function->source, &srcpos))); + ucv_object_add(entry, "byte", ucv_int64_new(srcpos)); } if (i > 1) { if (frame->closure) { - if (frame->closure->function->name) + if (frame->closure->function->name[0]) name = frame->closure->function->name; else if (frame->closure->is_arrow) name = "[arrow function]"; @@ -760,29 +800,29 @@ uc_vm_capture_stacktrace(uc_vm *vm, size_t i) name = frame->cfunction->name; } - json_object_object_add(entry, "function", xjs_new_string(name)); + ucv_object_add(entry, "function", ucv_string_new(name)); } - if (!json_object_equal(last, entry)) { - json_object_array_add(stacktrace, entry); + if (!ucv_equal(last, entry)) { + ucv_array_push(stacktrace, entry); last = entry; } else { - uc_value_put(entry); + ucv_put(entry); } } return stacktrace; } -static json_object * +static uc_value_t * uc_vm_get_error_context(uc_vm *vm) { - json_object *stacktrace; + uc_value_t *stacktrace; uc_callframe *frame; + uc_stringbuf_t *buf; uc_chunk *chunk; - size_t offset, len = 0, i; - char *msg = NULL; + size_t offset, i; /* skip to first non-native function call frame */ for (i = vm->callframes.count; i > 0; i--) @@ -795,17 +835,17 @@ uc_vm_get_error_context(uc_vm *vm) return NULL; chunk = uc_vm_frame_chunk(frame); - offset = uc_function_get_srcpos(frame->closure->function, (frame->ip - chunk->entries) - 1); + offset = ucv_function_srcpos((uc_value_t *)frame->closure->function, (frame->ip - chunk->entries) - 1); stacktrace = uc_vm_capture_stacktrace(vm, i); + buf = ucv_stringbuf_new(); + if (offset) - format_error_context(&msg, &len, frame->closure->function->source, stacktrace, offset); + format_error_context(buf, frame->closure->function->source, stacktrace, offset); else - xasprintf(&msg, "At offset %zu", (frame->ip - chunk->entries) - 1); + ucv_stringbuf_printf(buf, "At offset %zu", (frame->ip - chunk->entries) - 1); - json_object_object_add(json_object_array_get_idx(stacktrace, 0), "context", xjs_new_string(msg)); - - free(msg); + ucv_object_add(ucv_array_get(stacktrace, 0), "context", ucv_stringbuf_finish(buf)); return stacktrace; } @@ -823,7 +863,7 @@ uc_vm_raise_exception(uc_vm *vm, uc_exception_type_t type, const char *fmt, ...) xvasprintf(&vm->exception.message, fmt, ap); va_end(ap); - uc_value_put(vm->exception.stacktrace); + ucv_put(vm->exception.stacktrace); vm->exception.stacktrace = uc_vm_get_error_context(vm); } @@ -837,15 +877,15 @@ uc_vm_insn_load(uc_vm *vm, enum insn_type insn) break; case I_LOAD8: - uc_vm_stack_push(vm, xjs_new_int64(vm->arg.s8)); + uc_vm_stack_push(vm, ucv_int64_new(vm->arg.s8)); break; case I_LOAD16: - uc_vm_stack_push(vm, xjs_new_int64(vm->arg.s16)); + uc_vm_stack_push(vm, ucv_int64_new(vm->arg.s16)); break; case I_LOAD32: - uc_vm_stack_push(vm, xjs_new_int64(vm->arg.s32)); + uc_vm_stack_push(vm, ucv_int64_new(vm->arg.s32)); break; default: @@ -856,33 +896,33 @@ uc_vm_insn_load(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_load_regexp(uc_vm *vm, enum insn_type insn) { + uc_value_t *re, *jstr = uc_chunk_get_constant(uc_vm_current_chunk(vm), vm->arg.u32); bool icase = false, newline = false, global = false; - json_object *jstr = uc_chunk_get_constant(uc_vm_current_chunk(vm), vm->arg.u32); - const char *str; - uc_regexp *re; - char *err; + char *str, *err = NULL; - if (!json_object_is_type(jstr, json_type_string) || json_object_get_string_len(jstr) < 2) { + if (ucv_type(jstr) != UC_STRING || ucv_string_length(jstr) < 2) { uc_vm_stack_push(vm, NULL); - uc_value_put(jstr); + ucv_put(jstr); return; } - str = json_object_get_string(jstr); + str = ucv_string_get(jstr); global = (*str & (1 << 0)); icase = (*str & (1 << 1)); newline = (*str & (1 << 2)); - re = uc_regexp_new(++str, icase, newline, global, &err); + re = ucv_regexp_new(++str, icase, newline, global, &err); - uc_value_put(jstr); + ucv_put(jstr); if (re) - uc_vm_stack_push(vm, re->header.jso); + uc_vm_stack_push(vm, re); else uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, "%s", err); + + free(err); } static void @@ -894,29 +934,32 @@ uc_vm_insn_load_null(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_load_bool(uc_vm *vm, enum insn_type insn) { - uc_vm_stack_push(vm, xjs_new_boolean(insn == I_LTRUE)); + uc_vm_stack_push(vm, ucv_boolean_new(insn == I_LTRUE)); } static void uc_vm_insn_load_var(uc_vm *vm, enum insn_type insn) { - json_object *name, *val = NULL; - uc_prototype *scope, *next; + uc_value_t *name, *val = NULL; + uc_value_t *scope, *next; + bool found; scope = vm->globals; name = uc_chunk_get_constant(uc_vm_current_chunk(vm), vm->arg.u32); - while (json_object_get_type(name) == json_type_string) { - if (json_object_object_get_ex(scope->header.jso, json_object_get_string(name), &val)) + while (ucv_type(name) == UC_STRING) { + val = ucv_object_get(scope, ucv_string_get(name), &found); + + if (found) break; - next = scope->parent; + next = ucv_prototype_get(scope); if (!next) { if (vm->config->strict_declarations) { uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, "access to undeclared variable %s", - json_object_get_string(name)); + ucv_string_get(name)); } break; @@ -925,21 +968,21 @@ uc_vm_insn_load_var(uc_vm *vm, enum insn_type insn) scope = next; } - uc_value_put(name); + ucv_put(name); - uc_vm_stack_push(vm, uc_value_get(val)); + uc_vm_stack_push(vm, ucv_get(val)); } static void uc_vm_insn_load_val(uc_vm *vm, enum insn_type insn) { - json_object *k = uc_vm_stack_pop(vm); - json_object *v = uc_vm_stack_pop(vm); + uc_value_t *k = uc_vm_stack_pop(vm); + uc_value_t *v = uc_vm_stack_pop(vm); - switch (json_object_get_type(v)) { - case json_type_object: - case json_type_array: - uc_vm_stack_push(vm, uc_getval(v, k)); + switch (ucv_type(v)) { + case UC_OBJECT: + case UC_ARRAY: + uc_vm_stack_push(vm, uc_getval(vm, v, k)); break; default: @@ -950,21 +993,20 @@ uc_vm_insn_load_val(uc_vm *vm, enum insn_type insn) break; } - - uc_value_put(k); - uc_value_put(v); + ucv_put(k); + ucv_put(v); } static void uc_vm_insn_load_upval(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); - uc_upvalref *ref = frame->closure->upvals[vm->arg.u32]; + uc_upvalref_t *ref = frame->closure->upvals[vm->arg.u32]; if (ref->closed) - uc_vm_stack_push(vm, uc_value_get(ref->value)); + uc_vm_stack_push(vm, ucv_get(ref->value)); else - uc_vm_stack_push(vm, uc_value_get(vm->stack.entries[ref->slot])); + uc_vm_stack_push(vm, ucv_get(vm->stack.entries[ref->slot])); } static void @@ -972,15 +1014,16 @@ uc_vm_insn_load_local(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); - uc_vm_stack_push(vm, uc_value_get(vm->stack.entries[frame->stackframe + vm->arg.u32])); + uc_vm_stack_push(vm, ucv_get(vm->stack.entries[frame->stackframe + vm->arg.u32])); } -static uc_upvalref * +static uc_upvalref_t * uc_vm_capture_upval(uc_vm *vm, size_t slot) { - uc_upvalref *curr = vm->open_upvals; - uc_upvalref *prev = NULL; - uc_upvalref *created; + uc_upvalref_t *curr = vm->open_upvals; + uc_upvalref_t *prev = NULL; + uc_upvalref_t *created; + char *s; while (curr && curr->slot > slot) { prev = curr; @@ -988,23 +1031,23 @@ uc_vm_capture_upval(uc_vm *vm, size_t slot) } if (curr && curr->slot == slot) { - if (vm->trace) - fprintf(stderr, " {+%zu} <%p> %s\n", - slot, - curr, - json_object_to_json_string(vm->stack.entries[slot])); + if (vm->trace) { + s = ucv_to_string(NULL, vm->stack.entries[slot]); + fprintf(stderr, " {+%zu} <%p> %s\n", slot, curr, s); + free(s); + } return curr; } - created = uc_upvalref_new(slot); + created = (uc_upvalref_t *)ucv_upvalref_new(slot); created->next = curr; - if (vm->trace) - fprintf(stderr, " {*%zu} <%p> %s\n", - slot, - created, - json_object_to_json_string(vm->stack.entries[slot])); + if (vm->trace) { + s = ucv_to_string(NULL, vm->stack.entries[slot]); + fprintf(stderr, " {*%zu} <%p> %s\n", slot, created, s); + free(s); + } if (prev) prev->next = created; @@ -1017,21 +1060,22 @@ uc_vm_capture_upval(uc_vm *vm, size_t slot) static void uc_vm_close_upvals(uc_vm *vm, size_t slot) { - uc_upvalref *ref; + uc_upvalref_t *ref; + char *s; while (vm->open_upvals && vm->open_upvals->slot >= slot) { ref = vm->open_upvals; - ref->value = uc_value_get(vm->stack.entries[ref->slot]); + ref->value = ucv_get(vm->stack.entries[ref->slot]); ref->closed = true; - if (vm->trace) - fprintf(stderr, " {!%zu} <%p> %s\n", - ref->slot, - ref, - json_object_to_json_string(ref->value)); + if (vm->trace) { + s = ucv_to_string(NULL, ref->value); + fprintf(stderr, " {!%zu} <%p> %s\n", ref->slot, ref, s); + free(s); + } vm->open_upvals = ref->next; - json_object_put(ref->header.jso); + ucv_put(&ref->header); } } @@ -1039,13 +1083,13 @@ static void uc_vm_insn_load_closure(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); - json_object *fno = uc_chunk_get_constant(uc_vm_current_chunk(vm), vm->arg.u32); - uc_function *function = uc_object_as_function(fno); - uc_closure *closure = uc_closure_new(function, insn == I_ARFN); + uc_value_t *fno = uc_chunk_get_constant(uc_vm_current_chunk(vm), vm->arg.u32); + uc_function_t *function = (uc_function_t *)fno; + uc_closure_t *closure = (uc_closure_t *)ucv_closure_new(vm, function, insn == I_ARFN); volatile int32_t uv; size_t i; - uc_vm_stack_push(vm, closure->header.jso); + uc_vm_stack_push(vm, &closure->header); for (i = 0; i < function->nupvals; i++) { uv = ( @@ -1060,7 +1104,7 @@ uc_vm_insn_load_closure(uc_vm *vm, enum insn_type insn) else closure->upvals[i] = frame->closure->upvals[uv]; - uc_value_get(closure->upvals[i]->header.jso); + ucv_get(&closure->upvals[i]->header); frame->ip += 4; } @@ -1069,23 +1113,26 @@ uc_vm_insn_load_closure(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_store_var(uc_vm *vm, enum insn_type insn) { - json_object *name, *v = uc_vm_stack_pop(vm); - uc_prototype *scope, *next; + uc_value_t *name, *v = uc_vm_stack_pop(vm); + uc_value_t *scope, *next; + bool found; scope = vm->globals; name = uc_chunk_get_constant(uc_vm_current_chunk(vm), vm->arg.u32); - while (json_object_get_type(name) == json_type_string) { - if (json_object_object_get_ex(scope->header.jso, json_object_get_string(name), NULL)) + while (ucv_type(name) == UC_STRING) { + ucv_object_get(scope, ucv_string_get(name), &found); + + if (found) break; - next = scope->parent; + next = ucv_prototype_get(scope); if (!next) { if (vm->config->strict_declarations) { uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, "access to undeclared variable %s", - json_object_get_string(name)); + ucv_string_get(name)); } break; @@ -1094,53 +1141,45 @@ uc_vm_insn_store_var(uc_vm *vm, enum insn_type insn) scope = next; } - if (scope && json_object_get_type(name) == json_type_string) - json_object_object_add(scope->header.jso, json_object_get_string(name), uc_value_get(v)); + if (scope && ucv_type(name) == UC_STRING) + ucv_object_add(scope, ucv_string_get(name), ucv_get(v)); - uc_value_put(name); + ucv_put(name); uc_vm_stack_push(vm, v); } static void uc_vm_insn_store_val(uc_vm *vm, enum insn_type insn) { - json_object *v = uc_vm_stack_pop(vm); - json_object *k = uc_vm_stack_pop(vm); - json_object *o = uc_vm_stack_pop(vm); - - const char *typenames[] = { - [json_type_string] = "string", - [json_type_int] = "integer", - [json_type_double] = "double", - [json_type_boolean] = "boolean", - [json_type_null] = "null" - }; + uc_value_t *v = uc_vm_stack_pop(vm); + uc_value_t *k = uc_vm_stack_pop(vm); + uc_value_t *o = uc_vm_stack_pop(vm); - switch (json_object_get_type(o)) { - case json_type_object: - case json_type_array: - uc_vm_stack_push(vm, uc_setval(o, k, v)); + switch (ucv_type(o)) { + case UC_OBJECT: + case UC_ARRAY: + uc_vm_stack_push(vm, uc_setval(vm, o, k, v)); break; default: uc_vm_raise_exception(vm, EXCEPTION_TYPE, "attempt to set property on %s value", - typenames[json_object_get_type(o)]); + ucv_typename(o)); } - uc_value_put(o); - uc_value_put(k); + ucv_put(o); + ucv_put(k); } static void uc_vm_insn_store_upval(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); - uc_upvalref *ref = frame->closure->upvals[vm->arg.u32]; - json_object *val = uc_value_get(uc_vm_stack_peek(vm, 0)); + uc_upvalref_t *ref = frame->closure->upvals[vm->arg.u32]; + uc_value_t *val = ucv_get(uc_vm_stack_peek(vm, 0)); if (ref->closed) { - uc_value_put(ref->value); + ucv_put(ref->value); ref->value = val; } else { @@ -1152,43 +1191,43 @@ static void uc_vm_insn_store_local(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); - json_object *val = uc_value_get(uc_vm_stack_peek(vm, 0)); + uc_value_t *val = ucv_get(uc_vm_stack_peek(vm, 0)); uc_vm_stack_set(vm, frame->stackframe + vm->arg.u32, val); } -static json_object * -uc_vm_value_bitop(uc_vm *vm, enum insn_type operation, json_object *value, json_object *operand) +static uc_value_t * +uc_vm_value_bitop(uc_vm *vm, enum insn_type operation, uc_value_t *value, uc_value_t *operand) { - json_object *rv = NULL; + uc_value_t *rv = NULL; int64_t n1, n2; double d; - if (uc_cast_number(value, &n1, &d) == json_type_double) + if (uc_cast_number(value, &n1, &d) == UC_DOUBLE) n1 = isnan(d) ? 0 : (int64_t)d; - if (uc_cast_number(operand, &n2, &d) == json_type_double) + if (uc_cast_number(operand, &n2, &d) == UC_DOUBLE) n2 = isnan(d) ? 0 : (int64_t)d; switch (operation) { case I_LSHIFT: - rv = xjs_new_int64(n1 << n2); + rv = ucv_int64_new(n1 << n2); break; case I_RSHIFT: - rv = xjs_new_int64(n1 >> n2); + rv = ucv_int64_new(n1 >> n2); break; case I_BAND: - rv = xjs_new_int64(n1 & n2); + rv = ucv_int64_new(n1 & n2); break; case I_BXOR: - rv = xjs_new_int64(n1 ^ n2); + rv = ucv_int64_new(n1 ^ n2); break; case I_BOR: - rv = xjs_new_int64(n1 | n2); + rv = ucv_int64_new(n1 | n2); break; default: @@ -1198,32 +1237,32 @@ uc_vm_value_bitop(uc_vm *vm, enum insn_type operation, json_object *value, json_ return rv; } -static json_object * -uc_vm_value_arith(uc_vm *vm, enum insn_type operation, json_object *value, json_object *operand) +static uc_value_t * +uc_vm_value_arith(uc_vm *vm, enum insn_type operation, uc_value_t *value, uc_value_t *operand) { - json_object *rv = NULL; - enum json_type t1, t2; - const char *s1, *s2; + uc_value_t *rv = NULL; + uc_type_t t1, t2; + char *s, *s1, *s2; size_t len1, len2; int64_t n1, n2; double d1, d2; - char *s; if (operation > I_MOD) return uc_vm_value_bitop(vm, operation, value, operand); - if (operation == I_ADD && - (json_object_is_type(value, json_type_string) || - json_object_is_type(operand, json_type_string))) { - s1 = value ? json_object_get_string(value) : "null"; - s2 = operand ? json_object_get_string(operand) : "null"; - len1 = strlen(s1); - len2 = strlen(s2); + if (operation == I_ADD && (ucv_type(value) == UC_STRING || ucv_type(operand) == UC_STRING)) { + s1 = (ucv_type(value) != UC_STRING) ? ucv_to_string(vm, value) : NULL; + s2 = (ucv_type(operand) != UC_STRING) ? ucv_to_string(vm, operand) : NULL; + len1 = s1 ? strlen(s1) : ucv_string_length(value); + len2 = s2 ? strlen(s2) : ucv_string_length(operand); s = xalloc(len1 + len2 + 1); - snprintf(s, len1 + len2 + 1, "%s%s", s1, s2); + memcpy(s, s1 ? s1 : ucv_string_get(value), len1); + memcpy(s + len1, s2 ? s2 : ucv_string_get(operand), len2); + free(s1); + free(s2); - rv = xjs_new_string(s); + rv = ucv_string_new_length(s, len1 + len2); free(s); @@ -1233,38 +1272,38 @@ uc_vm_value_arith(uc_vm *vm, enum insn_type operation, json_object *value, json_ t1 = uc_cast_number(value, &n1, &d1); t2 = uc_cast_number(operand, &n2, &d2); - if (t1 == json_type_double || t2 == json_type_double) { - d1 = (t1 == json_type_double) ? d1 : (double)n1; - d2 = (t2 == json_type_double) ? d2 : (double)n2; + if (t1 == UC_DOUBLE || t2 == UC_DOUBLE) { + d1 = (t1 == UC_DOUBLE) ? d1 : (double)n1; + d2 = (t2 == UC_DOUBLE) ? d2 : (double)n2; switch (operation) { case I_ADD: case I_PLUS: - rv = uc_double_new(d1 + d2); + rv = ucv_double_new(d1 + d2); break; case I_SUB: - rv = uc_double_new(d1 - d2); + rv = ucv_double_new(d1 - d2); break; case I_MUL: - rv = uc_double_new(d1 * d2); + rv = ucv_double_new(d1 * d2); break; case I_DIV: if (d2 == 0.0) - rv = uc_double_new(INFINITY); + rv = ucv_double_new(INFINITY); else if (isnan(d2)) - rv = uc_double_new(NAN); + rv = ucv_double_new(NAN); else if (!isfinite(d2)) - rv = uc_double_new(isfinite(d1) ? 0.0 : NAN); + rv = ucv_double_new(isfinite(d1) ? 0.0 : NAN); else - rv = uc_double_new(d1 / d2); + rv = ucv_double_new(d1 / d2); break; case I_MOD: - rv = uc_double_new(NAN); + rv = ucv_double_new(NAN); break; default: @@ -1278,27 +1317,27 @@ uc_vm_value_arith(uc_vm *vm, enum insn_type operation, json_object *value, json_ switch (operation) { case I_ADD: case I_PLUS: - rv = xjs_new_int64(n1 + n2); + rv = ucv_int64_new(n1 + n2); break; case I_SUB: - rv = xjs_new_int64(n1 - n2); + rv = ucv_int64_new(n1 - n2); break; case I_MUL: - rv = xjs_new_int64(n1 * n2); + rv = ucv_int64_new(n1 * n2); break; case I_DIV: if (n2 == 0) - rv = uc_double_new(INFINITY); + rv = ucv_double_new(INFINITY); else - rv = xjs_new_int64(n1 / n2); + rv = ucv_int64_new(n1 / n2); break; case I_MOD: - rv = xjs_new_int64(n1 % n2); + rv = ucv_int64_new(n1 % n2); break; default: @@ -1315,25 +1354,28 @@ uc_vm_value_arith(uc_vm *vm, enum insn_type operation, json_object *value, json_ static void uc_vm_insn_update_var(uc_vm *vm, enum insn_type insn) { - json_object *name, *val, *inc = uc_vm_stack_pop(vm); - uc_prototype *scope, *next; + uc_value_t *name, *val, *inc = uc_vm_stack_pop(vm); + uc_value_t *scope, *next; + bool found; scope = vm->globals; name = uc_chunk_get_constant(uc_vm_current_chunk(vm), vm->arg.u32 & 0x00FFFFFF); - assert(json_object_is_type(name, json_type_string)); + assert(ucv_type(name) == UC_STRING); + + while (true) { + val = ucv_object_get(scope, ucv_string_get(name), &found); - while (true) { - if (json_object_object_get_ex(scope->header.jso, json_object_get_string(name), &val)) + if (found) break; - next = scope->parent; + next = ucv_prototype_get(scope); if (!next) { if (vm->config->strict_declarations) { uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, "access to undeclared variable %s", - json_object_get_string(name)); + ucv_string_get(name)); } break; @@ -1344,26 +1386,26 @@ uc_vm_insn_update_var(uc_vm *vm, enum insn_type insn) val = uc_vm_value_arith(vm, vm->arg.u32 >> 24, val, inc); - json_object_object_add(scope->header.jso, json_object_get_string(name), uc_value_get(val)); + ucv_object_add(scope, ucv_string_get(name), ucv_get(val)); uc_vm_stack_push(vm, val); - uc_value_put(name); - uc_value_put(inc); + ucv_put(name); + ucv_put(inc); } static void uc_vm_insn_update_val(uc_vm *vm, enum insn_type insn) { - json_object *inc = uc_vm_stack_pop(vm); - json_object *k = uc_vm_stack_pop(vm); - json_object *v = uc_vm_stack_pop(vm); - json_object *val = NULL; + uc_value_t *inc = uc_vm_stack_pop(vm); + uc_value_t *k = uc_vm_stack_pop(vm); + uc_value_t *v = uc_vm_stack_pop(vm); + uc_value_t *val = NULL; - switch (json_object_get_type(v)) { - case json_type_object: - case json_type_array: - val = uc_getval(v, k); - uc_vm_stack_push(vm, uc_setval(v, k, uc_vm_value_arith(vm, vm->arg.u8, val, inc))); + switch (ucv_type(v)) { + case UC_OBJECT: + case UC_ARRAY: + val = uc_getval(vm, v, k); + uc_vm_stack_push(vm, uc_setval(vm, v, k, uc_vm_value_arith(vm, vm->arg.u8, val, inc))); break; default: @@ -1374,10 +1416,10 @@ uc_vm_insn_update_val(uc_vm *vm, enum insn_type insn) break; } - uc_value_put(val); - uc_value_put(inc); - uc_value_put(v); - uc_value_put(k); + ucv_put(val); + ucv_put(inc); + ucv_put(v); + ucv_put(k); } static void @@ -1385,9 +1427,9 @@ uc_vm_insn_update_upval(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); size_t slot = vm->arg.u32 & 0x00FFFFFF; - uc_upvalref *ref = frame->closure->upvals[slot]; - json_object *inc = uc_vm_stack_pop(vm); - json_object *val; + uc_upvalref_t *ref = frame->closure->upvals[slot]; + uc_value_t *inc = uc_vm_stack_pop(vm); + uc_value_t *val; if (ref->closed) val = ref->value; @@ -1398,14 +1440,14 @@ uc_vm_insn_update_upval(uc_vm *vm, enum insn_type insn) uc_vm_stack_push(vm, val); - uc_value_put(inc); + ucv_put(inc); if (ref->closed) { - uc_value_put(ref->value); - ref->value = uc_value_get(uc_vm_stack_peek(vm, 0)); + ucv_put(ref->value); + ref->value = ucv_get(uc_vm_stack_peek(vm, 0)); } else { - uc_vm_stack_set(vm, ref->slot, uc_value_get(uc_vm_stack_peek(vm, 0))); + uc_vm_stack_set(vm, ref->slot, ucv_get(uc_vm_stack_peek(vm, 0))); } } @@ -1414,22 +1456,22 @@ uc_vm_insn_update_local(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); size_t slot = vm->arg.u32 & 0x00FFFFFF; - json_object *inc = uc_vm_stack_pop(vm); - json_object *val; + uc_value_t *inc = uc_vm_stack_pop(vm); + uc_value_t *val; val = uc_vm_value_arith(vm, vm->arg.u32 >> 24, vm->stack.entries[frame->stackframe + slot], inc); uc_vm_stack_push(vm, val); - uc_value_put(inc); - uc_vm_stack_set(vm, frame->stackframe + slot, uc_value_get(uc_vm_stack_peek(vm, 0))); + ucv_put(inc); + uc_vm_stack_set(vm, frame->stackframe + slot, ucv_get(uc_vm_stack_peek(vm, 0))); } static void uc_vm_insn_narr(uc_vm *vm, enum insn_type insn) { - json_object *arr = xjs_new_array_size(vm->arg.u32); + uc_value_t *arr = ucv_array_new_length(vm, vm->arg.u32); uc_vm_stack_push(vm, arr); } @@ -1437,11 +1479,11 @@ uc_vm_insn_narr(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_parr(uc_vm *vm, enum insn_type insn) { - json_object *arr = uc_vm_stack_peek(vm, vm->arg.u32); + uc_value_t *arr = uc_vm_stack_peek(vm, vm->arg.u32); size_t idx; for (idx = 0; idx < vm->arg.u32; idx++) - json_object_array_add(arr, uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1)); + ucv_array_push(arr, uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1)); for (idx = 0; idx < vm->arg.u32; idx++) uc_vm_stack_pop(vm); @@ -1452,79 +1494,81 @@ uc_vm_insn_parr(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_marr(uc_vm *vm, enum insn_type insn) { - json_object *src = uc_vm_stack_pop(vm); - json_object *dst = uc_vm_stack_peek(vm, 0); + uc_value_t *src = uc_vm_stack_pop(vm); + uc_value_t *dst = uc_vm_stack_peek(vm, 0); size_t i; + char *s; - if (!json_object_is_type(src, json_type_array)) { - uc_vm_raise_exception(vm, EXCEPTION_TYPE, - "(%s) is not iterable", - json_object_to_json_string(src)); + if (ucv_type(src) != UC_ARRAY) { + s = ucv_to_string(vm, src); + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "(%s) is not iterable", s); + ucv_put(src); + free(s); return; } - for (i = 0; i < json_object_array_length(src); i++) - json_object_array_add(dst, uc_value_get(json_object_array_get_idx(src, i))); + for (i = 0; i < ucv_array_length(src); i++) + ucv_array_push(dst, ucv_get(ucv_array_get(src, i))); - uc_value_put(src); + ucv_put(src); } static void uc_vm_insn_nobj(uc_vm *vm, enum insn_type insn) { - json_object *arr = xjs_new_object(); + uc_value_t *obj = ucv_object_new(vm); - uc_vm_stack_push(vm, arr); + uc_vm_stack_push(vm, obj); } static void uc_vm_insn_sobj(uc_vm *vm, enum insn_type insn) { - json_object *obj = uc_vm_stack_peek(vm, vm->arg.u32); + uc_value_t *obj = uc_vm_stack_peek(vm, vm->arg.u32); size_t idx; for (idx = 0; idx < vm->arg.u32; idx += 2) { - json_object_object_add(obj, - json_object_get_string(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1)), - uc_value_get(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 2))); + ucv_object_add(obj, + ucv_string_get(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1)), + ucv_get(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 2))); } for (idx = 0; idx < vm->arg.u32; idx++) - uc_value_put(uc_vm_stack_pop(vm)); + ucv_put(uc_vm_stack_pop(vm)); } static void uc_vm_insn_mobj(uc_vm *vm, enum insn_type insn) { - json_object *src = uc_vm_stack_pop(vm); - json_object *dst = uc_vm_stack_peek(vm, 0); - char *istr; + uc_value_t *src = uc_vm_stack_pop(vm); + uc_value_t *dst = uc_vm_stack_peek(vm, 0); size_t i; + char *s; - switch (json_object_get_type(src)) { - case json_type_object: + switch (ucv_type(src)) { + case UC_OBJECT: ; /* a label can only be part of a statement and a declaration is not a statement */ - json_object_object_foreach(src, k, v) - json_object_object_add(dst, k, uc_value_get(v)); + ucv_object_foreach(src, k, v) + ucv_object_add(dst, k, ucv_get(v)); - uc_value_put(src); + ucv_put(src); break; case json_type_array: - for (i = 0; i < json_object_array_length(src); i++) { - xasprintf(&istr, "%zu", i); - json_object_object_add(dst, istr, uc_value_get(json_object_array_get_idx(src, i))); - free(istr); + for (i = 0; i < ucv_array_length(src); i++) { + xasprintf(&s, "%zu", i); + ucv_object_add(dst, s, ucv_get(ucv_array_get(src, i))); + free(s); } - uc_value_put(src); + ucv_put(src); break; default: - uc_vm_raise_exception(vm, EXCEPTION_TYPE, - "Value (%s) is not iterable", - json_object_to_json_string(src)); + s = ucv_to_string(vm, src); + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Value (%s) is not iterable", s); + free(s); break; } @@ -1533,14 +1577,14 @@ uc_vm_insn_mobj(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_arith(uc_vm *vm, enum insn_type insn) { - json_object *r2 = uc_vm_stack_pop(vm); - json_object *r1 = uc_vm_stack_pop(vm); - json_object *rv; + uc_value_t *r2 = uc_vm_stack_pop(vm); + uc_value_t *r1 = uc_vm_stack_pop(vm); + uc_value_t *rv; rv = uc_vm_value_arith(vm, insn, r1, r2); - uc_value_put(r1); - uc_value_put(r2); + ucv_put(r1); + ucv_put(r2); uc_vm_stack_push(vm, rv); } @@ -1548,23 +1592,23 @@ uc_vm_insn_arith(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_plus_minus(uc_vm *vm, enum insn_type insn) { - struct json_object *v = uc_vm_stack_pop(vm); + uc_value_t *v = uc_vm_stack_pop(vm); bool is_sub = (insn == I_MINUS); - enum json_type t; + uc_type_t t; int64_t n; double d; t = uc_cast_number(v, &n, &d); - json_object_put(v); + ucv_put(v); switch (t) { - case json_type_int: - uc_vm_stack_push(vm, xjs_new_int64(is_sub ? -n : n)); + case UC_INTEGER: + uc_vm_stack_push(vm, ucv_int64_new(is_sub ? -n : n)); break; default: - uc_vm_stack_push(vm, uc_double_new(is_sub ? -d : d)); + uc_vm_stack_push(vm, ucv_double_new(is_sub ? -d : d)); break; } } @@ -1572,14 +1616,14 @@ uc_vm_insn_plus_minus(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_bitop(uc_vm *vm, enum insn_type insn) { - json_object *r2 = uc_vm_stack_pop(vm); - json_object *r1 = uc_vm_stack_pop(vm); - json_object *rv; + uc_value_t *r2 = uc_vm_stack_pop(vm); + uc_value_t *r1 = uc_vm_stack_pop(vm); + uc_value_t *rv; rv = uc_vm_value_bitop(vm, insn, r1, r2); - uc_value_put(r1); - uc_value_put(r2); + ucv_put(r1); + ucv_put(r2); uc_vm_stack_push(vm, rv); } @@ -1587,23 +1631,23 @@ uc_vm_insn_bitop(uc_vm *vm, enum insn_type insn) static void uc_vm_insn_complement(uc_vm *vm, enum insn_type insn) { - struct json_object *v = uc_vm_stack_pop(vm); + uc_value_t *v = uc_vm_stack_pop(vm); int64_t n; double d; - if (uc_cast_number(v, &n, &d) == json_type_double) + if (uc_cast_number(v, &n, &d) == UC_DOUBLE) n = isnan(d) ? 0 : (int64_t)d; - json_object_put(v); + ucv_put(v); - uc_vm_stack_push(vm, xjs_new_int64(~n)); + uc_vm_stack_push(vm, ucv_int64_new(~n)); } static void uc_vm_insn_rel(uc_vm *vm, enum insn_type insn) { - json_object *r2 = uc_vm_stack_pop(vm); - json_object *r1 = uc_vm_stack_pop(vm); + uc_value_t *r2 = uc_vm_stack_pop(vm); + uc_value_t *r1 = uc_vm_stack_pop(vm); bool res = false; switch (insn) { @@ -1627,27 +1671,27 @@ uc_vm_insn_rel(uc_vm *vm, enum insn_type insn) break; } - uc_value_put(r1); - uc_value_put(r2); + ucv_put(r1); + ucv_put(r2); - uc_vm_stack_push(vm, xjs_new_boolean(res)); + uc_vm_stack_push(vm, ucv_boolean_new(res)); } static void uc_vm_insn_in(uc_vm *vm, enum insn_type insn) { - json_object *r2 = uc_vm_stack_pop(vm); - json_object *r1 = uc_vm_stack_pop(vm); - json_object *item; + uc_value_t *r2 = uc_vm_stack_pop(vm); + uc_value_t *r1 = uc_vm_stack_pop(vm); + uc_value_t *item; size_t arrlen, arridx; bool found = false; - const char *key; + char *key; - switch (json_object_get_type(r2)) { - case json_type_array: - for (arridx = 0, arrlen = json_object_array_length(r2); + switch (ucv_type(r2)) { + case UC_ARRAY: + for (arridx = 0, arrlen = ucv_array_length(r2); arridx < arrlen; arridx++) { - item = json_object_array_get_idx(r2, arridx); + item = ucv_array_get(r2, arridx); if (uc_cmp(TK_EQ, r1, item)) { found = true; @@ -1657,41 +1701,53 @@ uc_vm_insn_in(uc_vm *vm, enum insn_type insn) break; - case json_type_object: - key = r1 ? json_object_get_string(r1) : "null"; - found = json_object_object_get_ex(r2, key, NULL); + case UC_OBJECT: + if (ucv_type(r1) == UC_STRING) { + ucv_object_get(r2, ucv_string_get(r1), &found); + } + else { + key = ucv_to_string(vm, r1); + ucv_object_get(r2, key, &found); + free(key); + } + break; default: found = false; } - uc_value_put(r1); - uc_value_put(r2); + ucv_put(r1); + ucv_put(r2); - uc_vm_stack_push(vm, xjs_new_boolean(found)); + uc_vm_stack_push(vm, ucv_boolean_new(found)); } static void uc_vm_insn_equality(uc_vm *vm, enum insn_type insn) { - json_object *r2 = uc_vm_stack_pop(vm); - json_object *r1 = uc_vm_stack_pop(vm); - bool equal = uc_eq(r1, r2); + uc_value_t *r2 = uc_vm_stack_pop(vm); + uc_value_t *r1 = uc_vm_stack_pop(vm); + bool equal; + + if (ucv_is_scalar(r1) && ucv_is_scalar(r2)) + equal = ucv_equal(r1, r2); + else + equal = (r1 == r2); - uc_value_put(r1); - uc_value_put(r2); + ucv_put(r1); + ucv_put(r2); - uc_vm_stack_push(vm, xjs_new_boolean((insn == I_EQS) ? equal : !equal)); + uc_vm_stack_push(vm, ucv_boolean_new((insn == I_EQS) ? equal : !equal)); } static void uc_vm_insn_not(uc_vm *vm, enum insn_type insn) { - json_object *r1 = uc_vm_stack_pop(vm); + uc_value_t *r1 = uc_vm_stack_pop(vm); - uc_vm_stack_push(vm, xjs_new_boolean(!uc_val_is_truish(r1))); - uc_value_put(r1); + uc_vm_stack_push(vm, ucv_boolean_new(!uc_val_is_truish(r1))); + ucv_put(r1); } static void @@ -1718,7 +1774,7 @@ uc_vm_insn_jmpz(uc_vm *vm, enum insn_type insn) { uc_callframe *frame = uc_vm_current_frame(vm); uc_chunk *chunk = uc_vm_frame_chunk(frame); - json_object *v = uc_vm_stack_pop(vm); + uc_value_t *v = uc_vm_stack_pop(vm); int32_t addr = vm->arg.s32; /* ip already has been incremented */ @@ -1733,56 +1789,64 @@ uc_vm_insn_jmpz(uc_vm *vm, enum insn_type insn) if (!uc_val_is_truish(v)) frame->ip += addr; - uc_value_put(v); + ucv_put(v); } static void uc_vm_insn_next(uc_vm *vm, enum insn_type insn) { - json_object *k = uc_vm_stack_pop(vm); - json_object *v = uc_vm_stack_pop(vm); + uc_value_t *k = uc_vm_stack_pop(vm); + uc_value_t *v = uc_vm_stack_pop(vm); + void *end = (void *)~(uintptr_t)0; + uc_ressource_t *iterk; struct lh_entry *curr; - int64_t n; + uint64_t n; + + if (k != NULL && ucv_type(k) != UC_RESSOURCE) { + fprintf(stderr, "Invalid iterator value\n"); + abort(); + } + + if (k == NULL) { + k = ucv_ressource_new(NULL, NULL); + ((uc_ressource_t *)k)->type = UINT64_MAX; + } - switch (json_object_get_type(v)) { - case json_type_object: - curr = k ? json_object_get_userdata(k) : json_object_get_object(v)->head; + iterk = (uc_ressource_t *)k; - if (curr) { - if (!k) - k = xjs_new_string("[key]"); + switch (ucv_type(v)) { + case UC_OBJECT: + curr = iterk->data ? iterk->data : ((uc_object_t *)v)->table->head; - json_object_set_userdata(k, curr->next, NULL); + if (curr != NULL && curr != end) { + iterk->data = curr->next ? curr->next : end; - uc_vm_stack_push(vm, xjs_new_string(curr->k)); + uc_vm_stack_push(vm, ucv_string_new(curr->k)); if (insn == I_NEXTKV) - uc_vm_stack_push(vm, uc_value_get((json_object *)curr->v)); + uc_vm_stack_push(vm, ucv_get((uc_value_t *)curr->v)); uc_vm_stack_push(vm, k); - uc_value_put(v); + ucv_put(v); return; } break; - case json_type_array: - if (!k) - k = xjs_new_int64(0); - - n = json_object_get_int64(k); + case UC_ARRAY: + n = (uintptr_t)iterk->data; - if (json_object_is_type(k, json_type_int) && n < json_object_array_length(v)) { - json_object_int_inc(k, 1); + if (n < ucv_array_length(v)) { + iterk->data = (void *)(n + 1); if (insn == I_NEXTKV) - uc_vm_stack_push(vm, xjs_new_int64(n)); + uc_vm_stack_push(vm, ucv_uint64_new(n)); - uc_vm_stack_push(vm, uc_value_get(json_object_array_get_idx(v, n))); + uc_vm_stack_push(vm, ucv_get(ucv_array_get(v, n))); uc_vm_stack_push(vm, k); - uc_value_put(v); + ucv_put(v); return; } @@ -1799,87 +1863,83 @@ uc_vm_insn_next(uc_vm *vm, enum insn_type insn) if (insn == I_NEXTKV) uc_vm_stack_push(vm, NULL); - uc_value_put(k); - uc_value_put(v); + ucv_put(k); + ucv_put(v); } static void uc_vm_insn_close_upval(uc_vm *vm, enum insn_type insn) { uc_vm_close_upvals(vm, vm->stack.count - 1); - uc_value_put(uc_vm_stack_pop(vm)); + ucv_put(uc_vm_stack_pop(vm)); } static void uc_vm_insn_call(uc_vm *vm, enum insn_type insn) { - json_object *fno = uc_value_get(uc_vm_stack_peek(vm, vm->arg.u32 & 0xffff)); - json_object *ctx = NULL; + uc_value_t *fno = ucv_get(uc_vm_stack_peek(vm, vm->arg.u32 & 0xffff)); + uc_value_t *ctx = NULL; - if (!uc_object_is_type(fno, UC_OBJ_CLOSURE) || !uc_object_as_closure(fno)->is_arrow) + if (!ucv_is_arrowfn(fno)) ctx = NULL; else if (vm->callframes.count > 0) - ctx = uc_value_get(uc_vm_current_frame(vm)->ctx); + ctx = uc_vm_current_frame(vm)->ctx; - uc_vm_call_function(vm, ctx, fno, false, vm->arg.u32); + uc_vm_call_function(vm, ucv_get(ctx), fno, false, vm->arg.u32); } static void uc_vm_insn_mcall(uc_vm *vm, enum insn_type insn) { size_t key_slot = vm->stack.count - (vm->arg.u32 & 0xffff) - 1; - json_object *ctx = vm->stack.entries[key_slot - 1]; - json_object *key = vm->stack.entries[key_slot]; - json_object *fno = uc_getval(ctx, key); + uc_value_t *ctx = vm->stack.entries[key_slot - 1]; + uc_value_t *key = vm->stack.entries[key_slot]; + uc_value_t *fno = uc_getval(vm, ctx, key); uc_vm_stack_set(vm, key_slot, fno); /* arrow functions as method calls inherit the parent ctx */ - if (uc_object_is_type(fno, UC_OBJ_CLOSURE) && uc_object_as_closure(fno)->is_arrow) + if (ucv_is_arrowfn(fno)) ctx = uc_vm_current_frame(vm)->ctx; - uc_vm_call_function(vm, uc_value_get(ctx), uc_value_get(fno), true, vm->arg.u32); + uc_vm_call_function(vm, ucv_get(ctx), ucv_get(fno), true, vm->arg.u32); } static void uc_vm_insn_print(uc_vm *vm, enum insn_type insn) { - json_object *v = uc_vm_stack_pop(vm); - const char *p; - size_t len; + uc_value_t *v = uc_vm_stack_pop(vm); + char *p; - switch (json_object_get_type(v)) { - case json_type_object: - case json_type_array: - p = json_object_to_json_string_ext(v, JSON_C_TO_STRING_NOSLASHESCAPE|JSON_C_TO_STRING_SPACED); - len = strlen(p); + switch (ucv_type(v)) { + case UC_OBJECT: + case UC_ARRAY: + p = ucv_to_jsonstring(vm, v); + fwrite(p, 1, strlen(p), stdout); + free(p); break; - case json_type_string: - p = json_object_get_string(v); - len = json_object_get_string_len(v); + case UC_STRING: + fwrite(ucv_string_get(v), 1, ucv_string_length(v), stdout); break; - case json_type_null: - p = ""; - len = 0; + case UC_NULL: break; default: - p = json_object_get_string(v); - len = strlen(p); + p = ucv_to_string(vm, v); + fwrite(p, 1, strlen(p), stdout); + free(p); } - fwrite(p, 1, len, stdout); - - uc_value_put(v); + ucv_put(v); } -static json_object * +static uc_value_t * uc_vm_callframe_pop(uc_vm *vm) { uc_callframe *frame = uc_vm_current_frame(vm); - json_object *retval; + uc_value_t *retval; /* close upvalues */ uc_vm_close_upvals(vm, frame->stackframe); @@ -1891,18 +1951,18 @@ uc_vm_callframe_pop(uc_vm *vm) /* reset function stack frame */ while (vm->stack.count > frame->stackframe) - uc_value_put(uc_vm_stack_pop(vm)); + ucv_put(uc_vm_stack_pop(vm)); /* for method calls, release context as well */ if (frame->mcall) - uc_value_put(uc_vm_stack_pop(vm)); + ucv_put(uc_vm_stack_pop(vm)); /* release function */ - uc_value_put(frame->closure ? frame->closure->header.jso : NULL); - uc_value_put(frame->cfunction ? frame->cfunction->header.jso : NULL); + ucv_put((uc_value_t *)frame->closure); + ucv_put((uc_value_t *)frame->cfunction); /* release context */ - uc_value_put(frame->ctx); + ucv_put(frame->ctx); vm->callframes.count--; @@ -1920,9 +1980,9 @@ uc_vm_output_exception(uc_vm *vm) vm->exception.message); fprintf(stderr, "%s\n\n", - json_object_get_string( - json_object_object_get( - json_object_array_get_idx(vm->exception.stacktrace, 0), "context"))); + ucv_string_get( + ucv_object_get( + ucv_array_get(vm->exception.stacktrace, 0), "context", NULL))); } static uc_vm_status_t @@ -1930,7 +1990,7 @@ uc_vm_execute_chunk(uc_vm *vm) { uc_callframe *frame = uc_vm_current_frame(vm); uc_chunk *chunk = uc_vm_frame_chunk(frame); - json_object *retval; + uc_value_t *retval; enum insn_type insn; while (chunk) { @@ -1961,7 +2021,7 @@ uc_vm_execute_chunk(uc_vm *vm) break; case I_LTHIS: - uc_vm_stack_push(vm, uc_value_get(frame->ctx)); + uc_vm_stack_push(vm, ucv_get(frame->ctx)); break; case I_LVAR: @@ -2100,11 +2160,11 @@ uc_vm_execute_chunk(uc_vm *vm) break; case I_COPY: - uc_vm_stack_push(vm, uc_value_get(uc_vm_stack_peek(vm, vm->arg.u8))); + uc_vm_stack_push(vm, ucv_get(uc_vm_stack_peek(vm, vm->arg.u8))); break; case I_POP: - uc_value_put(uc_vm_stack_pop(vm)); + ucv_put(uc_vm_stack_pop(vm)); break; case I_CUPV: @@ -2127,7 +2187,7 @@ uc_vm_execute_chunk(uc_vm *vm) retval = uc_vm_callframe_pop(vm); if (vm->callframes.count == 0) { - uc_value_put(retval); + ucv_put(retval); return STATUS_OK; } @@ -2163,7 +2223,7 @@ uc_vm_execute_chunk(uc_vm *vm) return ERROR_RUNTIME; /* no exception handler in current function, pop callframe */ - uc_value_put(uc_vm_callframe_pop(vm)); + ucv_put(uc_vm_callframe_pop(vm)); /* resume execution at topmost remaining callframe */ frame = uc_vector_last(&vm->callframes); @@ -2176,25 +2236,25 @@ uc_vm_execute_chunk(uc_vm *vm) } static uc_vm_status_t -uc_vm_preload(uc_vm *vm, json_object *modules) +uc_vm_preload(uc_vm *vm, uc_value_t *modules) { - json_object *requirefn, *module, *name; + uc_value_t *requirefn, *module, *name; uc_exception_type_t ex; size_t i; - if (!json_object_is_type(modules, json_type_array)) + if (ucv_type(modules) != UC_ARRAY) return STATUS_OK; - requirefn = uc_prototype_lookup(vm->globals, "require"); + requirefn = ucv_property_get(vm->globals, "require"); - if (!uc_object_is_type(requirefn, UC_OBJ_CFUNCTION)) + if (ucv_type(requirefn) != UC_CFUNCTION) return STATUS_OK; - for (i = 0; i < json_object_array_length(modules); i++) { - name = json_object_array_get_idx(modules, i); + for (i = 0; i < ucv_array_length(modules); i++) { + name = ucv_array_get(modules, i); - uc_vm_stack_push(vm, uc_value_get(requirefn)); - uc_vm_stack_push(vm, uc_value_get(name)); + uc_vm_stack_push(vm, ucv_get(requirefn)); + uc_vm_stack_push(vm, ucv_get(name)); ex = uc_vm_call(vm, false, 1); @@ -2203,21 +2263,22 @@ uc_vm_preload(uc_vm *vm, json_object *modules) module = uc_vm_stack_pop(vm); - uc_value_put(uc_setval(vm->globals->header.jso, name, module)); + ucv_put(uc_setval(vm, vm->globals, name, module)); } return STATUS_OK; } uc_vm_status_t -uc_vm_execute(uc_vm *vm, uc_function *fn, uc_prototype *globals, json_object *modules) +uc_vm_execute(uc_vm *vm, uc_function_t *fn, uc_value_t *globals, uc_value_t *modules) { - uc_closure *closure = uc_closure_new(fn, false); + uc_closure_t *closure = (uc_closure_t *)ucv_closure_new(vm, fn, false); uc_callframe *frame; + uc_stringbuf_t *buf; uc_vm_status_t rv; vm->globals = globals; - uc_value_get(globals ? globals->header.jso : NULL); + ucv_get(globals); uc_vector_grow(&vm->callframes); @@ -2227,14 +2288,12 @@ uc_vm_execute(uc_vm *vm, uc_function *fn, uc_prototype *globals, json_object *mo frame->ip = uc_vm_frame_chunk(frame)->entries; if (vm->trace) { - size_t msglen = 0; - char *msg = NULL; + buf = xprintbuf_new(); - format_source_context(&msg, &msglen, - fn->source, 0, true); + format_source_context(buf, fn->source, 0, true); - fprintf(stderr, "%s", msg); - free(msg); + fwrite(buf->buf, 1, printbuf_length(buf), stderr); + printbuf_free(buf); uc_vm_frame_dump(vm, frame); } @@ -2249,7 +2308,7 @@ uc_vm_execute(uc_vm *vm, uc_function *fn, uc_prototype *globals, json_object *mo else rv = uc_vm_execute_chunk(vm); - uc_value_put(vm->globals->header.jso); + ucv_put(vm->globals); vm->globals = NULL; return rv; @@ -2258,11 +2317,11 @@ uc_vm_execute(uc_vm *vm, uc_function *fn, uc_prototype *globals, json_object *mo uc_exception_type_t uc_vm_call(uc_vm *vm, bool mcall, size_t nargs) { - json_object *ctx = mcall ? uc_value_get(uc_vm_stack_peek(vm, nargs - 1)) : NULL; - json_object *fno = uc_value_get(uc_vm_stack_peek(vm, nargs)); + uc_value_t *ctx = mcall ? ucv_get(uc_vm_stack_peek(vm, nargs + 1)) : NULL; + uc_value_t *fno = ucv_get(uc_vm_stack_peek(vm, nargs)); if (uc_vm_call_function(vm, ctx, fno, mcall, nargs & 0xffff)) { - if (!uc_object_is_type(fno, UC_OBJ_CFUNCTION)) + if (ucv_type(fno) != UC_CFUNCTION) uc_vm_execute_chunk(vm); } diff --git a/vm.h b/vm.h index 5672cbb..e4de4f9 100644 --- a/vm.h +++ b/vm.h @@ -21,7 +21,6 @@ #include #include "chunk.h" -#include "object.h" #include "util.h" #include "lexer.h" @@ -102,7 +101,6 @@ typedef struct { int8_t stack_pop; int8_t stack_push; int8_t operand_bytes; - bool operand_is_skip; } uc_insn_definition; typedef enum { @@ -116,30 +114,31 @@ typedef enum { typedef struct { uc_exception_type_t type; - json_object *stacktrace; + uc_value_t *stacktrace; char *message; } uc_exception; typedef struct { uint8_t *ip; - uc_closure *closure; - uc_cfunction *cfunction; + uc_closure_t *closure; + uc_cfunction_t *cfunction; size_t stackframe; - json_object *ctx; + uc_value_t *ctx; bool mcall; } uc_callframe; uc_declare_vector(uc_callframes, uc_callframe); -uc_declare_vector(uc_stack, json_object *); +uc_declare_vector(uc_stack, uc_value_t *); typedef struct uc_vm { uc_stack stack; uc_exception exception; uc_callframes callframes; - uc_upvalref *open_upvals; + uc_upvalref_t *open_upvals; uc_parse_config *config; - uc_prototype *globals; + uc_value_t *globals; uc_source *sources; + uc_weakref_t values; union { uint32_t u32; int32_t s32; @@ -163,15 +162,15 @@ extern uint32_t insns[__I_MAX]; void uc_vm_init(uc_vm *vm, uc_parse_config *config); void uc_vm_free(uc_vm *vm); -void uc_vm_stack_push(uc_vm *vm, json_object *value); -json_object *uc_vm_stack_pop(uc_vm *vm); -json_object *uc_vm_stack_peek(uc_vm *vm, size_t offset); +void uc_vm_stack_push(uc_vm *vm, uc_value_t *value); +uc_value_t *uc_vm_stack_pop(uc_vm *vm); +uc_value_t *uc_vm_stack_peek(uc_vm *vm, size_t offset); uc_exception_type_t uc_vm_call(uc_vm *vm, bool mcall, size_t nargs); void __attribute__((format(printf, 3, 0))) uc_vm_raise_exception(uc_vm *vm, uc_exception_type_t type, const char *fmt, ...); -uc_vm_status_t uc_vm_execute(uc_vm *vm, uc_function *fn, uc_prototype *globals, json_object *modules); +uc_vm_status_t uc_vm_execute(uc_vm *vm, uc_function_t *fn, uc_value_t *globals, uc_value_t *modules); #endif /* __VM_H_ */ -- cgit v1.2.3 From 4cf897c200e72ba5d8823af3fcd515ac92651b99 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 11:33:11 +0200 Subject: lib: uc_system(): fix invalid free() of non-heap memory Signed-off-by: Jo-Philipp Wich --- lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib.c b/lib.c index f4e69c7..5733646 100644 --- a/lib.c +++ b/lib.c @@ -2127,9 +2127,9 @@ uc_system(uc_vm *vm, size_t nargs) switch (ucv_type(cmdline)) { case UC_STRING: arglist = xalloc(sizeof(*arglist) * 4); - arglist[0] = "/bin/sh"; - arglist[1] = "-c"; - arglist[2] = ucv_string_get(cmdline); + arglist[0] = xstrdup("/bin/sh"); + arglist[1] = xstrdup("-c"); + arglist[2] = ucv_to_string(vm, cmdline); arglist[3] = NULL; break; -- cgit v1.2.3 From 7b287279d933e69168b99a643ecb91631fd4f992 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 11:57:29 +0200 Subject: main: fix ineffective EOF check in parse() Signed-off-by: Jo-Philipp Wich --- main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index e762312..618d5e4 100644 --- a/main.c +++ b/main.c @@ -99,8 +99,8 @@ parse(uc_parse_config *config, uc_source *src, uc_value_t *globals = NULL; uc_function_t *entry; uc_vm vm = {}; - char c, c2, *err; - int rc = 0; + int c, c2, rc = 0; + char *err; uc_vm_init(&vm, config); -- cgit v1.2.3 From c51934a9ab7d416b5ef25c28efd2cf693c4c1f3a Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 11:39:46 +0200 Subject: types: fix potential leak of key in ucv_object_add() Signed-off-by: Jo-Philipp Wich --- types.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/types.c b/types.c index de2860f..85023c6 100644 --- a/types.c +++ b/types.c @@ -779,6 +779,7 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) struct lh_entry *existing_entry; uc_value_t *existing_value; unsigned long hash; + void *k; if (ucv_type(uv) != UC_OBJECT) return false; @@ -787,7 +788,15 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) existing_entry = lh_table_lookup_entry_w_hash(object->table, (const void *)key, hash); if (existing_entry == NULL) { - return (lh_table_insert_w_hash(object->table, xstrdup(key), val, hash, 0) == 0); + k = xstrdup(key); + + if (lh_table_insert_w_hash(object->table, k, val, hash, 0) != 0) { + free(k); + + return false; + } + + return true; } existing_value = (uc_value_t *)lh_entry_v(existing_entry); -- cgit v1.2.3 From 9c5106a6b76527af3b164f81cea8e2dede46ea4d Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 11:52:00 +0200 Subject: types: fix potential memory leaks and null pointer accesses Signed-off-by: Jo-Philipp Wich --- types.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/types.c b/types.c index 85023c6..1247268 100644 --- a/types.c +++ b/types.c @@ -1152,7 +1152,7 @@ uc_value_t * ucv_from_json(uc_vm *vm, json_object *jso) { //uc_array_t *arr; - uc_value_t *uv; + uc_value_t *uv, *item; int64_t n; size_t i; @@ -1177,8 +1177,19 @@ ucv_from_json(uc_vm *vm, json_object *jso) case json_type_object: uv = ucv_object_new(vm); - json_object_object_foreach(jso, key, val) - ucv_object_add(uv, key, ucv_from_json(vm, val)); + json_object_object_foreach(jso, key, val) { + item = ucv_from_json(vm, val); + + if (!ucv_object_add(uv, key, item)) + ucv_put(item); + +#ifdef __clang_analyzer__ + /* Clang static analyzer does not understand that the object retains + * our item so pretend to free it here to suppress the false positive + * memory leak warning. */ + ucv_put(item); +#endif + } return uv; @@ -1193,8 +1204,19 @@ ucv_from_json(uc_vm *vm, json_object *jso) */ uv = ucv_array_new(vm); - for (i = 0; i < json_object_array_length(jso); i++) - ucv_array_push(uv, ucv_from_json(vm, json_object_array_get_idx(jso, i))); + for (i = 0; i < json_object_array_length(jso); i++) { + item = ucv_from_json(vm, json_object_array_get_idx(jso, i)); + + if (!ucv_array_push(uv, item)) + ucv_put(item); + +#ifdef __clang_analyzer__ + /* Clang static analyzer does not understand that the array retains + * our item so pretend to free it here to suppress the false positive + * memory leak warning. */ + ucv_put(item); +#endif + } return uv; @@ -1284,7 +1306,7 @@ ucv_to_string_json_encoded(uc_stringbuf_t *pb, const char *s, size_t len, bool r if (!regexp) ucv_stringbuf_append(pb, "\""); - for (i = 0; i < len; i++, s++) { + for (i = 0; s != NULL && i < len; i++, s++) { switch (*s) { case '"': ucv_stringbuf_append(pb, "\\\""); @@ -1340,6 +1362,8 @@ ucv_call_tostring(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) uc_value_t *proto = ucv_prototype_get(uv); uc_value_t *tostr = ucv_object_get(proto, "tostring", NULL); uc_value_t *str; + size_t l; + char *s; if (!ucv_is_callable(tostr)) return false; @@ -1353,10 +1377,13 @@ ucv_call_tostring(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) str = uc_vm_stack_pop(vm); if (ucv_type(str) == UC_STRING) { + s = ucv_string_get(str); + l = ucv_string_length(str); + if (json) - ucv_to_string_json_encoded(pb, ucv_string_get(str), ucv_string_length(str), false); - else - ucv_stringbuf_addstr(pb, ucv_string_get(str), ucv_string_length(str)); + ucv_to_string_json_encoded(pb, s, l, false); + else if (s) + ucv_stringbuf_addstr(pb, s, l); } else if (json) { ucv_stringbuf_append(pb, "\"\""); @@ -1384,8 +1411,9 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) uc_regexp_t *regexp; uc_value_t *argname; uc_array_t *array; - size_t i; + size_t i, l; double d; + char *s; if (ucv_is_marked(uv)) { ucv_stringbuf_append(pb, "null"); @@ -1438,10 +1466,16 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) break; case UC_STRING: - if (json) - ucv_to_string_json_encoded(pb, ucv_string_get(uv), ucv_string_length(uv), false); - else - ucv_stringbuf_addstr(pb, ucv_string_get(uv), ucv_string_length(uv)); + s = ucv_string_get(uv); + l = ucv_string_length(uv); + + if (s) { + if (json) + ucv_to_string_json_encoded(pb, s, l, false); + else + ucv_stringbuf_addstr(pb, s, l); + } + break; case UC_ARRAY: @@ -1530,12 +1564,19 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) if (i == function->nargs && function->vararg) ucv_stringbuf_append(pb, "..."); - if (argname) - ucv_stringbuf_addstr(pb, ucv_string_get(argname), ucv_string_length(argname)); - else - ucv_stringbuf_printf(pb, "[arg%zu]", i); + if (argname) { + s = ucv_string_get(argname); + l = ucv_string_length(argname); + + if (s) + ucv_stringbuf_addstr(pb, s, l); + + ucv_put(argname); - ucv_put(argname); + continue; + } + + ucv_stringbuf_printf(pb, "[arg%zu]", i); } ucv_stringbuf_printf(pb, ")%s { ... }%s", @@ -1619,6 +1660,7 @@ ucv_equal(uc_value_t *uv1, uc_value_t *uv2) { uc_type_t t1 = ucv_type(uv1); uc_type_t t2 = ucv_type(uv2); + const char *s1, *s2; uint64_t u1, u2; int64_t n1, n2; bool b1, b2; @@ -1661,13 +1703,15 @@ ucv_equal(uc_value_t *uv1, uc_value_t *uv2) return false; case UC_STRING: + s1 = ucv_string_get(uv1); + s2 = ucv_string_get(uv2); u1 = ucv_string_length(uv1); u2 = ucv_string_length(uv2); - if (u1 != u2) + if (s1 == NULL || s2 == NULL || u1 != u2) return false; - return (memcmp(ucv_string_get(uv1), ucv_string_get(uv2), u1) == 0); + return (memcmp(s1, s2, u1) == 0); case UC_ARRAY: u1 = ucv_array_length(uv1); -- cgit v1.2.3 From 28825acae8ee95543d4dc4f2c38e9711d4d4a420 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 13:46:26 +0200 Subject: types: support creating ressource values without associated type Signed-off-by: Jo-Philipp Wich --- types.c | 4 ++-- vm.c | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/types.c b/types.c index 1247268..4e17025 100644 --- a/types.c +++ b/types.c @@ -963,7 +963,7 @@ ucv_ressource_type_add(const char *name, uc_value_t *proto, void (*freefn)(void static uc_ressource_type_t * ucv_ressource_type_get(size_t type) { - return (type < res_types.count) ? &res_types.entries[type] : NULL; + return (type > 0 && type <= res_types.count) ? &res_types.entries[type - 1] : NULL; } uc_ressource_type_t * @@ -987,7 +987,7 @@ ucv_ressource_new(uc_ressource_type_t *type, void *data) res = xalloc(sizeof(*res)); res->header.type = UC_RESSOURCE; res->header.refcount = 1; - res->type = type - res_types.entries; + res->type = type ? (type - res_types.entries) + 1 : 0; res->data = data; return &res->header; diff --git a/vm.c b/vm.c index 10e3286..88c537c 100644 --- a/vm.c +++ b/vm.c @@ -1807,10 +1807,8 @@ uc_vm_insn_next(uc_vm *vm, enum insn_type insn) abort(); } - if (k == NULL) { + if (k == NULL) k = ucv_ressource_new(NULL, NULL); - ((uc_ressource_t *)k)->type = UINT64_MAX; - } iterk = (uc_ressource_t *)k; -- cgit v1.2.3 From d5dd183f3622002fbc4ae175045a3ebce4eeeb05 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 14:16:25 +0200 Subject: treewide: address various sign-compare warnings Signed-off-by: Jo-Philipp Wich --- lib.c | 4 ++-- types.c | 4 ++-- value.c | 8 ++++---- vm.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib.c b/lib.c index 5733646..80c1386 100644 --- a/lib.c +++ b/lib.c @@ -888,7 +888,7 @@ uc_splice(uc_vm *vm, size_t nargs) if (remlen < 0) remlen = 0; } - else if ((uint64_t)remlen > arrlen - ofs) { + else if ((uint64_t)remlen > arrlen - (uint64_t)ofs) { remlen = arrlen - ofs; } @@ -1025,7 +1025,7 @@ uc_substr(uc_vm *vm, size_t nargs) if (sublen < 0) sublen = 0; } - else if ((uint64_t)sublen > len - ofs) { + else if ((uint64_t)sublen > len - (uint64_t)ofs) { sublen = len - ofs; } diff --git a/types.c b/types.c index 4e17025..e786fb1 100644 --- a/types.c +++ b/types.c @@ -356,7 +356,7 @@ ucv_stringbuf_new(void) } }; - printbuf_memappend_fast(sb, (char *)&ustr, sizeof(ustr)); + printbuf_memappend_fast(sb, (char *)&ustr, (int)sizeof(ustr)); return sb; } @@ -1397,7 +1397,7 @@ ucv_call_tostring(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) void _ucv_stringbuf_append(uc_stringbuf_t *pb, const char *str, size_t len) { - printbuf_memappend_fast(pb, str, len); + printbuf_memappend_fast(pb, str, (int)len); } void diff --git a/value.c b/value.c index 5fafefa..e53e317 100644 --- a/value.c +++ b/value.c @@ -348,7 +348,7 @@ add_num(uc_value_list *list, int64_t n) list->index[list->isize++] = (TAG_TYPE)(TAG_NUM | TAG_SET_NV(n)); } else { - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } @@ -400,7 +400,7 @@ add_dbl(uc_value_list *list, double d) { size_t sz = TAG_ALIGN(sizeof(d)); - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } @@ -450,7 +450,7 @@ add_str(uc_value_list *list, const char *s, size_t slen) sz = TAG_ALIGN(sizeof(uint32_t) + slen); - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } @@ -528,7 +528,7 @@ add_ptr(uc_value_list *list, void *ptr) { size_t sz = TAG_ALIGN(sizeof(ptr)); - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } diff --git a/vm.c b/vm.c index 88c537c..f7d2e09 100644 --- a/vm.c +++ b/vm.c @@ -1836,7 +1836,7 @@ uc_vm_insn_next(uc_vm *vm, enum insn_type insn) n = (uintptr_t)iterk->data; if (n < ucv_array_length(v)) { - iterk->data = (void *)(n + 1); + iterk->data = (void *)(uintptr_t)(n + 1); if (insn == I_NEXTKV) uc_vm_stack_push(vm, ucv_uint64_new(n)); -- cgit v1.2.3 From 6def9fcf1cdec117866877aa5e7241e23bdc8c23 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 18:56:41 +0200 Subject: tests: pass ucode library path through environment This is required for out-of-tree builds where the *.so file location cannot be derived from the path of the ucode executable. Signed-off-by: Jo-Philipp Wich --- tests/custom/CMakeLists.txt | 10 ++++++++-- tests/custom/run_tests.sh | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/custom/CMakeLists.txt b/tests/custom/CMakeLists.txt index d56cb99..c8007a0 100644 --- a/tests/custom/CMakeLists.txt +++ b/tests/custom/CMakeLists.txt @@ -3,7 +3,10 @@ ADD_TEST( COMMAND run_tests.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) -SET_PROPERTY(TEST custom APPEND PROPERTY ENVIRONMENT "UCODE_BIN=valgrind --quiet --leak-check=full $") +SET_PROPERTY(TEST custom APPEND PROPERTY ENVIRONMENT + "UCODE_BIN=valgrind --quiet --leak-check=full $" + "UCODE_LIB=${CMAKE_BINARY_DIR}" +) IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") ADD_TEST( @@ -12,5 +15,8 @@ IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) - SET_PROPERTY(TEST custom-san APPEND PROPERTY ENVIRONMENT "UCODE_BIN=$") + SET_PROPERTY(TEST custom-san APPEND PROPERTY ENVIRONMENT + "UCODE_BIN=$" + "UCODE_LIB=${CMAKE_BINARY_DIR}" + ) ENDIF() diff --git a/tests/custom/run_tests.sh b/tests/custom/run_tests.sh index b20404a..61b116f 100755 --- a/tests/custom/run_tests.sh +++ b/tests/custom/run_tests.sh @@ -5,6 +5,7 @@ topdir=$(readlink -f "$testdir/../..") line='........................................' ucode_bin=${UCODE_BIN:-"$topdir/ucode"} +ucode_lib=${UCODE_LIB:-"$topdir"} extract_sections() { local file=$1 @@ -53,7 +54,7 @@ run_testcase() { ( cd "$topdir" - $ucode_bin -e '{ "REQUIRE_SEARCH_PATH": [ "'"$topdir"'/*.so" ] }' -i - <"$in" >"$dir/res.out" 2>"$dir/res.err" + $ucode_bin -e '{ "REQUIRE_SEARCH_PATH": [ "'"$ucode_lib"'/*.so" ] }' -i - <"$in" >"$dir/res.out" 2>"$dir/res.err" ) touch "$dir/empty" -- cgit v1.2.3 From 9ef693edd6c9572c0ab0b1381fe5e65a8430e8fb Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 20:05:51 +0200 Subject: vm: improve context for early errors Print "Before start of program" for errors that are raised before entering main(), e.g. on module preloading failure. Signed-off-by: Jo-Philipp Wich --- tests/cram/test_basic.t | 4 ++-- vm.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/cram/test_basic.t b/tests/cram/test_basic.t index 5061d37..982d58a 100644 --- a/tests/cram/test_basic.t +++ b/tests/cram/test_basic.t @@ -37,13 +37,13 @@ check that ucode provides proper error messages: $ ucode -m foo -s '' Runtime error: No module named 'foo' could be found - At offset 0 + At start of program [1] $ touch moo; ucode -m foo -i moo Runtime error: No module named 'foo' could be found - At offset 0 + At start of program [1] diff --git a/vm.c b/vm.c index f7d2e09..7d60805 100644 --- a/vm.c +++ b/vm.c @@ -842,8 +842,10 @@ uc_vm_get_error_context(uc_vm *vm) if (offset) format_error_context(buf, frame->closure->function->source, stacktrace, offset); + else if (frame->ip != chunk->entries) + ucv_stringbuf_printf(buf, "At instruction %zu", (frame->ip - chunk->entries) - 1); else - ucv_stringbuf_printf(buf, "At offset %zu", (frame->ip - chunk->entries) - 1); + ucv_stringbuf_append(buf, "At start of program"); ucv_object_add(ucv_array_get(stacktrace, 0), "context", ucv_stringbuf_finish(buf)); -- cgit v1.2.3 From 4af803d76e4c08ff5661c2b37dbd333f3aba866d Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 20:06:50 +0200 Subject: build: output error messages on test failures Signed-off-by: Jo-Philipp Wich --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52d6262..c6bfec9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.17) include(CheckFunctionExists) include(CheckSymbolExists) @@ -104,6 +104,7 @@ IF(UNIT_TESTING) ENABLE_TESTING() ADD_DEFINITIONS(-DUNIT_TESTING) ADD_SUBDIRECTORY(tests) + LIST(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") ADD_EXECUTABLE(ucode-san ${UCODE_SOURCES}) -- cgit v1.2.3 From 64eec7f90e945696572ee076b75d1f35e8f2248a Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 27 Apr 2021 00:35:20 +0200 Subject: treewide: ISO C / pedantic compliance - Shuffle typedefs to avoid need for non-compliant forward declarations - Fix non-compliant empty struct initializers - Remove use of braced expressions - Remove use of anonymous unions - Avoid `void *` pointer arithmetic - Fix several warnings reported by gcc -pedantic mode and clang 11 compilation Signed-off-by: Jo-Philipp Wich --- CMakeLists.txt | 5 +- chunk.h | 28 +--------- compiler.c | 33 ++++++----- lexer.c | 77 +++++++++++++------------- lexer.h | 6 -- lib.c | 52 +++++++++++------ lib/uci.c | 14 ++--- main.c | 9 ++- source.h | 10 +--- tests/cram/test_basic.t | 5 +- types.c | 11 ++-- types.h | 144 +++++++++++++++++++++++++++++++++++++++++++----- util.h | 2 + value.c | 2 +- value.h | 15 +---- vm.c | 37 +++++++------ vm.h | 49 +--------------- 17 files changed, 278 insertions(+), 221 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6bfec9..0d5fe54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,8 +107,9 @@ IF(UNIT_TESTING) LIST(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") - ADD_EXECUTABLE(ucode-san ${UCODE_SOURCES}) - TARGET_LINK_LIBRARIES(ucode-san ${json}) + ADD_EXECUTABLE(ucode-san ${UCODE_SOURCES}) + SET_PROPERTY(TARGET ucode-san PROPERTY ENABLE_EXPORTS 1) + TARGET_LINK_LIBRARIES(ucode-san ${json}) TARGET_COMPILE_OPTIONS(ucode-san PRIVATE -g -fno-omit-frame-pointer -fsanitize=undefined,address,leak -fno-sanitize-recover=all) TARGET_LINK_OPTIONS(ucode-san PRIVATE -fsanitize=undefined,address,leak) ENDIF() diff --git a/chunk.h b/chunk.h index cad00f7..2a8fa3a 100644 --- a/chunk.h +++ b/chunk.h @@ -22,38 +22,12 @@ #include "value.h" #include "util.h" - - -typedef struct { - size_t from, to, target, slot; -} uc_ehrange; - -typedef struct { - size_t from, to, slot, nameidx; -} uc_varrange; - -uc_declare_vector(uc_ehranges, uc_ehrange); -uc_declare_vector(uc_variables, uc_varrange); -uc_declare_vector(uc_offsetinfo, uint8_t); - -typedef struct { - size_t count; - uint8_t *entries; - uc_value_list constants; - uc_ehranges ehranges; - struct { - uc_variables variables; - uc_value_list varnames; - uc_offsetinfo offsets; - } debuginfo; -} uc_chunk; +#include "types.h" void uc_chunk_init(uc_chunk *chunk); void uc_chunk_free(uc_chunk *chunk); size_t uc_chunk_add(uc_chunk *chunk, uint8_t byte, size_t line); -typedef struct uc_value_t uc_value_t; - ssize_t uc_chunk_add_constant(uc_chunk *chunk, uc_value_t *value); uc_value_t *uc_chunk_get_constant(uc_chunk *chunk, size_t idx); void uc_chunk_pop(uc_chunk *chunk); diff --git a/compiler.c b/compiler.c index 04ff8ee..fd5b3af 100644 --- a/compiler.c +++ b/compiler.c @@ -468,7 +468,12 @@ uc_compiler_emit_s32(uc_compiler *compiler, size_t srcpos, int32_t n) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t lineoff = uc_compiler_set_srcpos(compiler, srcpos); - uint32_t v = n + 0x7fffffff; + uint32_t v; + + if (n <= 0) + v = n + 0x7fffffff; + else + v = (uint32_t)n + 0x7fffffff; uc_chunk_add(chunk, v / 0x1000000, lineoff); uc_chunk_add(chunk, (v / 0x10000) % 0x100, 0); @@ -555,9 +560,9 @@ uc_compiler_get_jmpaddr(uc_compiler *compiler, size_t off) assert(off + 4 < chunk->count); return ( - chunk->entries[off + 1] * 0x1000000 + - chunk->entries[off + 2] * 0x10000 + - chunk->entries[off + 3] * 0x100 + + chunk->entries[off + 1] * 0x1000000UL + + chunk->entries[off + 2] * 0x10000UL + + chunk->entries[off + 3] * 0x100UL + chunk->entries[off + 4] ) - 0x7fffffff; } @@ -1065,7 +1070,7 @@ static bool uc_compiler_compile_arrowfn(uc_compiler *compiler, uc_value_t *args, bool restarg) { bool array = (ucv_type(args) == UC_ARRAY); - uc_compiler fncompiler = {}; + uc_compiler fncompiler = { 0 }; size_t i, pos, load_off; uc_function_t *fn; ssize_t slot; @@ -1283,7 +1288,7 @@ static void uc_compiler_compile_call(uc_compiler *compiler, bool assignable) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); - uc_jmplist spreads = {}; + uc_jmplist spreads = { 0 }; enum insn_type type; size_t i, nargs = 0; @@ -1422,7 +1427,7 @@ uc_compiler_compile_delimitted_block(uc_compiler *compiler, uc_tokentype_t endty static void uc_compiler_compile_function(uc_compiler *compiler, bool assignable) { - uc_compiler fncompiler = {}; + uc_compiler fncompiler = { 0 }; uc_value_t *name = NULL; ssize_t slot = -1, pos; uc_tokentype_t type; @@ -1891,7 +1896,7 @@ uc_compiler_compile_if(uc_compiler *compiler) uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t jmpz_off, jmp_off, i; bool expect_endif = false; - uc_jmplist elifs = {}; + uc_jmplist elifs = { 0 }; uc_tokentype_t type; /* parse & compile condition expression */ @@ -1996,7 +2001,7 @@ uc_compiler_compile_while(uc_compiler *compiler) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t cond_off, jmpz_off, end_off; - uc_patchlist p = {}; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; @@ -2044,7 +2049,7 @@ uc_compiler_compile_for_in(uc_compiler *compiler, bool local, uc_token *kvar, uc { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t skip_jmp, test_jmp, key_slot, val_slot; - uc_patchlist p = {}; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; @@ -2154,7 +2159,7 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t test_off = 0, incr_off, skip_off, cond_off = 0; - uc_patchlist p = {}; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; @@ -2254,7 +2259,7 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var) static void uc_compiler_compile_for(uc_compiler *compiler) { - uc_token keyvar = {}, valvar = {}; + uc_token keyvar = { 0 }, valvar = { 0 }; bool local; uc_compiler_parse_consume(compiler, TK_LPAREN); @@ -2309,8 +2314,8 @@ uc_compiler_compile_switch(uc_compiler *compiler) size_t i, test_jmp, skip_jmp, next_jmp, value_slot, default_off = 0; uc_chunk *chunk = uc_compiler_current_chunk(compiler); uc_locals *locals = &compiler->locals; - uc_jmplist cases = {}; - uc_patchlist p = {}; + uc_jmplist cases = { 0 }; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; diff --git a/lexer.c b/lexer.c index 74e0c4d..aaba314 100644 --- a/lexer.c +++ b/lexer.c @@ -38,7 +38,7 @@ struct keyword { union { double d; bool b; - }; + } u; }; struct token { @@ -46,7 +46,7 @@ struct token { union { uint32_t patn; char pat[4]; - }; + } u; unsigned plen; uc_token *(*parse)(uc_lexer *, bool); }; @@ -135,33 +135,33 @@ static const struct token tokens[] = { }; static const struct keyword reserved_words[] = { - { TK_ENDFUNC, "endfunction", 11, {} }, + { TK_ENDFUNC, "endfunction", 11, { 0 } }, { TK_DOUBLE, "Infinity", 8, { .d = INFINITY } }, - { TK_CONTINUE, "continue", 8, {} }, - { TK_ENDWHILE, "endwhile", 8, {} }, - { TK_FUNC, "function", 8, {} }, - { TK_DEFAULT, "default", 7, {} }, - { TK_RETURN, "return", 6, {} }, - { TK_ENDFOR, "endfor", 6, {} }, - { TK_SWITCH, "switch", 6, {} }, - { TK_LOCAL, "local", 5, {} }, - { TK_ENDIF, "endif", 5, {} }, - { TK_WHILE, "while", 5, {} }, - { TK_BREAK, "break", 5, {} }, - { TK_CATCH, "catch", 5, {} }, + { TK_CONTINUE, "continue", 8, { 0 } }, + { TK_ENDWHILE, "endwhile", 8, { 0 } }, + { TK_FUNC, "function", 8, { 0 } }, + { TK_DEFAULT, "default", 7, { 0 } }, + { TK_RETURN, "return", 6, { 0 } }, + { TK_ENDFOR, "endfor", 6, { 0 } }, + { TK_SWITCH, "switch", 6, { 0 } }, + { TK_LOCAL, "local", 5, { 0 } }, + { TK_ENDIF, "endif", 5, { 0 } }, + { TK_WHILE, "while", 5, { 0 } }, + { TK_BREAK, "break", 5, { 0 } }, + { TK_CATCH, "catch", 5, { 0 } }, { TK_BOOL, "false", 5, { .b = false } }, { TK_BOOL, "true", 4, { .b = true } }, - { TK_ELIF, "elif", 4, {} }, - { TK_ELSE, "else", 4, {} }, - { TK_THIS, "this", 4, {} }, - { TK_NULL, "null", 4, {} }, - { TK_CASE, "case", 4, {} }, + { TK_ELIF, "elif", 4, { 0 } }, + { TK_ELSE, "else", 4, { 0 } }, + { TK_THIS, "this", 4, { 0 } }, + { TK_NULL, "null", 4, { 0 } }, + { TK_CASE, "case", 4, { 0 } }, { TK_DOUBLE, "NaN", 3, { .d = NAN } }, - { TK_TRY, "try", 3, {} }, - { TK_FOR, "for", 3, {} }, - { TK_LOCAL, "let", 3, {} }, - { TK_IF, "if", 2, {} }, - { TK_IN, "in", 2, {} }, + { TK_TRY, "try", 3, { 0 } }, + { TK_FOR, "for", 3, { 0 } }, + { TK_LOCAL, "let", 3, { 0 } }, + { TK_IF, "if", 2, { 0 } }, + { TK_IN, "in", 2, { 0 } }, }; @@ -362,7 +362,7 @@ parse_comment(uc_lexer *lex, bool no_regexp) if (!buf_remaining(lex)) return emit_op(lex, lex->lastoff, TK_ERROR, ucv_string_new("Unterminated comment")); - if (!strcmp(tok->pat, "//")) { + if (!strcmp(tok->u.pat, "//")) { end = "\n"; elen = 1; } @@ -400,7 +400,7 @@ static uc_token * parse_string(uc_lexer *lex, bool no_regexp) { const struct token *tok = lex->tok; - char q = tok->pat[0]; + char q = tok->u.pat[0]; char *ptr, *c; uc_token *rv; int code; @@ -438,7 +438,7 @@ parse_string(uc_lexer *lex, bool no_regexp) default: lex->is_escape = false; - c = strchr("a\ab\be\ef\fn\nr\rt\tv\v", *ptr); + c = strchr("a\ab\be\033f\fn\nr\rt\tv\v", *ptr); if (c && *c >= 'a') { lookbehind_append(lex, c + 1, 1); @@ -725,7 +725,7 @@ parse_label(uc_lexer *lex, bool no_regexp) size_t i; if (!lex->lookbehind && tok->plen) - lookbehind_append(lex, tok->pat, tok->plen); + lookbehind_append(lex, tok->u.pat, tok->plen); if (!buf_remaining(lex) || (lex->bufstart[0] != '_' && !isalnum(lex->bufstart[0]))) { for (i = 0, word = &reserved_words[0]; i < ARRAY_SIZE(reserved_words); i++, word = &reserved_words[i]) { @@ -734,11 +734,11 @@ parse_label(uc_lexer *lex, bool no_regexp) switch (word->type) { case TK_DOUBLE: - rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_double_new(word->d)); + rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_double_new(word->u.d)); break; case TK_BOOL: - rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_boolean_new(word->b)); + rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_boolean_new(word->u.b)); break; default: @@ -803,7 +803,7 @@ parse_number(uc_lexer *lex, bool no_regexp) if (*e == '.' || *e == 'e' || *e == 'E') { d = strtod(lex->lookbehind, &e); - if (tok->pat[0] == '-') + if (tok->u.pat[0] == '-') d = -d; if (e > lex->lookbehind && *e == 0) @@ -812,7 +812,7 @@ parse_number(uc_lexer *lex, bool no_regexp) rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, ucv_string_new("Invalid number literal")); } else if (*e == 0) { - if (tok->pat[0] == '-') + if (tok->u.pat[0] == '-') n = (errno == ERANGE) ? INT64_MIN : -n; rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_NUMBER, ucv_int64_new(n)); @@ -857,7 +857,8 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) rem = lex->bufend - lex->bufstart; - memcpy(lex->buf, lex->bufstart, rem); + if (rem) + memcpy(lex->buf, lex->bufstart, rem); rlen = fread(lex->buf + rem, 1, lex->buflen - rem, fp); @@ -1042,8 +1043,8 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) c = buf_remaining(lex) ? lex->bufstart[0] : 0; - if (tok->plen ? ((search.n & masks[tok->plen]) == tok->patn) - : (c >= tok->pat[0] && c <= tok->pat[1])) { + if (tok->plen ? ((search.n & masks[tok->plen]) == tok->u.patn) + : (c >= tok->u.pat[0] && c <= tok->u.pat[1])) { lex->lastoff = lex->source->off; /* token has a parse method, switch state */ @@ -1067,7 +1068,7 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) else if ((lex->block == STATEMENTS && tok->type == TK_RSTM) || (lex->block == EXPRESSION && tok->type == TK_REXP)) { /* strip whitespace after block */ - if (tok->pat[0] == '-') + if (tok->u.pat[0] == '-') lex->modifier = MINUS; /* strip newline after statement block */ @@ -1207,7 +1208,7 @@ uc_get_tokenname(unsigned type) if (tokens[i].type != type) continue; - snprintf(buf, sizeof(buf), "'%s'", tokens[i].pat); + snprintf(buf, sizeof(buf), "'%s'", tokens[i].u.pat); return buf; } diff --git a/lexer.h b/lexer.h index f023fb6..069e9e0 100644 --- a/lexer.h +++ b/lexer.h @@ -126,12 +126,6 @@ typedef struct { size_t pos; } uc_token; -typedef struct { - bool lstrip_blocks; - bool trim_blocks; - bool strict_declarations; -} uc_parse_config; - typedef struct { uc_lex_state_t state; uc_parse_config *config; diff --git a/lib.c b/lib.c index 80c1386..5d6235d 100644 --- a/lib.c +++ b/lib.c @@ -35,6 +35,7 @@ #include "compiler.h" #include "vm.h" #include "lib.h" +#include "source.h" static void format_context_line(uc_stringbuf_t *buf, const char *line, size_t off, bool compact) @@ -454,9 +455,11 @@ static uc_value_t * uc_delete(uc_vm *vm, size_t nargs) { uc_value_t *obj = uc_get_arg(0); + uc_value_t *key = NULL; uc_value_t *rv = NULL; - const char *key; + bool freeable; size_t i; + char *k; if (ucv_type(obj) != UC_OBJECT) return NULL; @@ -464,10 +467,14 @@ uc_delete(uc_vm *vm, size_t nargs) for (i = 1; i < nargs; i++) { ucv_put(rv); - key = ucv_string_get(uc_get_arg(i)); - rv = ucv_get(ucv_object_get(obj, key ? key : "null", NULL)); + key = uc_get_arg(i); + k = uc_cast_string(vm, &key, &freeable); + rv = ucv_get(ucv_object_get(obj, k, NULL)); + + ucv_object_delete(obj, k); - ucv_object_delete(obj, key ? key : "null"); + if (freeable) + free(k); } return rv; @@ -482,7 +489,7 @@ uc_die(uc_vm *vm, size_t nargs) s = msg ? uc_cast_string(vm, &msg, &freeable) : "Died"; - uc_vm_raise_exception(vm, EXCEPTION_USER, s); + uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s); if (freeable) free(s); @@ -494,13 +501,19 @@ static uc_value_t * uc_exists(uc_vm *vm, size_t nargs) { uc_value_t *obj = uc_get_arg(0); - const char *key = ucv_string_get(uc_get_arg(1)); - bool found; + uc_value_t *key = uc_get_arg(1); + bool found, freeable; + char *k; if (ucv_type(obj) != UC_OBJECT) return false; - ucv_object_get(obj, key ? key : "null", &found); + k = uc_cast_string(vm, &key, &freeable); + + ucv_object_get(obj, k, &found); + + if (freeable) + free(k); return ucv_boolean_new(found); } @@ -516,8 +529,9 @@ uc_exit(uc_vm *vm, size_t nargs) static uc_value_t * uc_getenv(uc_vm *vm, size_t nargs) { - const char *key = ucv_string_get(uc_get_arg(0)); - char *val = key ? getenv(key) : NULL; + uc_value_t *key = uc_get_arg(0); + char *k = ucv_string_get(key); + char *val = k ? getenv(k) : NULL; return val ? ucv_string_new(val) : NULL; } @@ -562,16 +576,18 @@ uc_filter(uc_vm *vm, size_t nargs) static uc_value_t * uc_hex(uc_vm *vm, size_t nargs) { - const char *val = ucv_string_get(uc_get_arg(0)); + uc_value_t *val = uc_get_arg(0); + char *e, *v; int64_t n; - char *e; - if (!val || !isxdigit(*val)) + v = ucv_string_get(val); + + if (!v || !isxdigit(*v)) return ucv_double_new(NAN); - n = strtoll(val, &e, 16); + n = strtoll(v, &e, 16); - if (e == val || *e) + if (e == v || *e) return ucv_double_new(NAN); return ucv_int64_new(n); @@ -622,8 +638,10 @@ uc_keys(uc_vm *vm, size_t nargs) arr = ucv_array_new(vm); - ucv_object_foreach(obj, key, val) + ucv_object_foreach(obj, key, val) { + (void)val; ucv_array_push(arr, ucv_string_new(key)); + } return arr; } @@ -1423,7 +1441,7 @@ uc_require_so(uc_vm *vm, const char *path, uc_value_t **res) return true; } - init = dlsym(dlh, "uc_module_entry"); + *(void **)(&init) = dlsym(dlh, "uc_module_entry"); if (!init) { uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, diff --git a/lib/uci.c b/lib/uci.c index fba7dad..00a451c 100644 --- a/lib/uci.c +++ b/lib/uci.c @@ -255,7 +255,7 @@ uc_uci_get_any(uc_vm *vm, size_t nargs, bool all) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = uc_get_arg(2); - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (!c || !*c) @@ -330,7 +330,7 @@ uc_uci_get_first(uc_vm *vm, size_t nargs) struct uci_package *p = NULL; struct uci_section *sc; struct uci_element *e; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (ucv_type(conf) != UC_STRING || @@ -465,7 +465,7 @@ uc_uci_set(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = NULL, *val = NULL; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; bool is_list = false; size_t i; int rv; @@ -570,7 +570,7 @@ uc_uci_delete(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = uc_get_arg(2); - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (ucv_type(conf) != UC_STRING || @@ -605,7 +605,7 @@ uc_uci_rename(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = NULL, *val = NULL; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (ucv_type(conf) != UC_STRING || @@ -665,7 +665,7 @@ uc_uci_reorder(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *val = uc_get_arg(2); - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int64_t n; int rv; @@ -705,7 +705,7 @@ uc_uci_pkg_command(uc_vm *vm, size_t nargs, enum pkg_cmd cmd) uc_value_t *conf = uc_get_arg(0); struct uci_element *e, *tmp; struct uci_package *p; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv, res = UCI_OK; if (cmd != CMD_REVERT && conf) diff --git a/main.c b/main.c index 618d5e4..d291447 100644 --- a/main.c +++ b/main.c @@ -98,7 +98,7 @@ parse(uc_parse_config *config, uc_source *src, { uc_value_t *globals = NULL; uc_function_t *entry; - uc_vm vm = {}; + uc_vm vm = { 0 }; int c, c2, rc = 0; char *err; @@ -285,7 +285,12 @@ main(int argc, char **argv) if (source) fprintf(stderr, "Options -i and -s are exclusive\n"); - source = uc_source_new_buffer("[-s argument]", xstrdup(optarg), strlen(optarg)); + c = xstrdup(optarg); + source = uc_source_new_buffer("[-s argument]", c, strlen(c)); + + if (!source) + free(c); + break; case 'S': diff --git a/source.h b/source.h index 4ca01b3..6557a48 100644 --- a/source.h +++ b/source.h @@ -22,17 +22,9 @@ #include #include "util.h" +#include "types.h" -uc_declare_vector(uc_lineinfo, uint8_t); - -typedef struct { - char *filename, *buffer; - FILE *fp; - size_t usecount, off; - uc_lineinfo lineinfo; -} uc_source; - uc_source *uc_source_new_file(const char *path); uc_source *uc_source_new_buffer(const char *name, char *buf, size_t len); diff --git a/tests/cram/test_basic.t b/tests/cram/test_basic.t index 982d58a..3d4cd9e 100644 --- a/tests/cram/test_basic.t +++ b/tests/cram/test_basic.t @@ -35,7 +35,7 @@ check that ucode provides proper error messages: One of -i or -s is required [1] - $ ucode -m foo -s '' + $ ucode -m foo -s ' ' Runtime error: No module named 'foo' could be found At start of program @@ -53,6 +53,7 @@ check that ucode can load fs module: One of -i or -s is required [1] - $ ucode -m fs -s '' + $ ucode -m fs -s ' ' + (no-eol) $ touch moo; ucode -m fs -i moo diff --git a/types.c b/types.c index e786fb1..3ed8aaa 100644 --- a/types.c +++ b/types.c @@ -927,7 +927,7 @@ ucv_closure_new(uc_vm *vm, uc_function_t *function, bool arrow_fn) closure->header.refcount = 1; closure->function = function; closure->is_arrow = arrow_fn; - closure->upvals = function->nupvals ? ((void *)closure + ALIGN(sizeof(*closure))) : NULL; + closure->upvals = function->nupvals ? (uc_upvalref_t **)((uintptr_t)closure + ALIGN(sizeof(*closure))) : NULL; if (vm) ucv_ref(&vm->values, &closure->ref); @@ -1590,8 +1590,8 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) ucv_stringbuf_printf(pb, "%sfunction%s%s(...) { [native code] }%s", json ? "\"" : "", - cfunction->name ? " " : "", - cfunction->name ? cfunction->name : "", + cfunction->name[0] ? " " : "", + cfunction->name[0] ? cfunction->name : "", json ? "\"" : ""); break; @@ -1739,6 +1739,7 @@ ucv_equal(uc_value_t *uv1, uc_value_t *uv2) } ucv_object_foreach(uv2, key2, val2) { + (void)val2; ucv_object_get(uv1, key2, &b1); if (!b1) @@ -1772,7 +1773,7 @@ ucv_gc(uc_vm *vm, bool final) /* unref unreachable objects */ for (ref = vm->values.next; ref != &vm->values; ref = ref->next) { - val = (void *)ref - offsetof(uc_array_t, ref); + val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref)); if (ucv_is_marked(val)) ucv_clear_mark(val); @@ -1782,7 +1783,7 @@ ucv_gc(uc_vm *vm, bool final) /* free destroyed objects */ for (ref = vm->values.next, tmp = ref->next; ref != &vm->values; ref = tmp, tmp = tmp->next) { - val = (void *)ref - offsetof(uc_array_t, ref); + val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref)); if (val->type == UC_NULL) { ucv_unref(ref); diff --git a/types.h b/types.h index a8962da..9dab246 100644 --- a/types.h +++ b/types.h @@ -22,8 +22,10 @@ #include #include -#include "source.h" -#include "chunk.h" +#include "util.h" + + +/* Value types and generic value header */ typedef enum uc_type_t { UC_NULL, @@ -48,6 +50,58 @@ typedef struct uc_value_t { uint32_t refcount:26; } uc_value_t; + +/* Constant list defintions */ + +typedef struct { + size_t isize; + size_t dsize; + uint64_t *index; + char *data; +} uc_value_list; + + +/* Source buffer defintions */ + +uc_declare_vector(uc_lineinfo, uint8_t); + +typedef struct { + char *filename, *buffer; + FILE *fp; + size_t usecount, off; + uc_lineinfo lineinfo; +} uc_source; + + +/* Bytecode chunk defintions */ + +typedef struct { + size_t from, to, target, slot; +} uc_ehrange; + +typedef struct { + size_t from, to, slot, nameidx; +} uc_varrange; + +uc_declare_vector(uc_ehranges, uc_ehrange); +uc_declare_vector(uc_variables, uc_varrange); +uc_declare_vector(uc_offsetinfo, uint8_t); + +typedef struct { + size_t count; + uint8_t *entries; + uc_value_list constants; + uc_ehranges ehranges; + struct { + uc_variables variables; + uc_value_list varnames; + uc_offsetinfo offsets; + } debuginfo; +} uc_chunk; + + +/* Value type structures */ + typedef struct uc_weakref_t { struct uc_weakref_t *prev; struct uc_weakref_t *next; @@ -144,6 +198,69 @@ typedef struct { uc_declare_vector(uc_ressource_types_t, uc_ressource_type_t); + +/* Parser definitions */ + +typedef struct { + bool lstrip_blocks; + bool trim_blocks; + bool strict_declarations; +} uc_parse_config; + + +/* VM definitions */ + +typedef enum { + EXCEPTION_NONE, + EXCEPTION_SYNTAX, + EXCEPTION_RUNTIME, + EXCEPTION_TYPE, + EXCEPTION_REFERENCE, + EXCEPTION_USER +} uc_exception_type_t; + +typedef struct { + uc_exception_type_t type; + uc_value_t *stacktrace; + char *message; +} uc_exception; + +typedef struct { + uint8_t *ip; + uc_closure_t *closure; + uc_cfunction_t *cfunction; + size_t stackframe; + uc_value_t *ctx; + bool mcall; +} uc_callframe; + +uc_declare_vector(uc_callframes, uc_callframe); +uc_declare_vector(uc_stack, uc_value_t *); + +struct uc_vm { + uc_stack stack; + uc_exception exception; + uc_callframes callframes; + uc_upvalref_t *open_upvals; + uc_parse_config *config; + uc_value_t *globals; + uc_source *sources; + uc_weakref_t values; + union { + uint32_t u32; + int32_t s32; + uint16_t u16; + int16_t s16; + uint8_t u8; + int8_t s8; + } arg; + size_t spread_values; + uint8_t trace; +}; + + +/* Value API */ + typedef struct printbuf uc_stringbuf_t; void ucv_free(uc_value_t *, bool); @@ -162,7 +279,7 @@ uc_value_t *ucv_string_new_length(const char *, size_t); size_t ucv_string_length(uc_value_t *); char *_ucv_string_get(uc_value_t **); -#define ucv_string_get(uv) ({ uc_value_t * volatile p = (uv); _ucv_string_get((uc_value_t **)&p); }) +#define ucv_string_get(uv) _ucv_string_get((uc_value_t **)&uv) uc_stringbuf_t *ucv_stringbuf_new(void); uc_value_t *ucv_stringbuf_finish(uc_stringbuf_t *); @@ -200,16 +317,17 @@ bool ucv_object_add(uc_value_t *, const char *, uc_value_t *); bool ucv_object_delete(uc_value_t *, const char *); size_t ucv_object_length(uc_value_t *); -#define ucv_object_foreach(obj, key, val) \ - char *key; \ - uc_value_t *val __attribute__((__unused__)); \ - for (struct lh_entry *entry ## key = (ucv_type(obj) == UC_OBJECT) ? ((uc_object_t *)obj)->table->head : NULL, *entry_next ## key = NULL; \ - ({ if (entry ## key) { \ - key = (char *)entry ## key->k; \ - val = (uc_value_t *)entry ## key->v; \ - entry_next ## key = entry ## key->next; \ - } ; entry ## key; }); \ - entry ## key = entry_next ## key) +#define ucv_object_foreach(obj, key, val) \ + char *key = NULL; \ + uc_value_t *val = NULL; \ + struct lh_entry *entry##key; \ + struct lh_entry *entry_next##key = NULL; \ + for (entry##key = (ucv_type(obj) == UC_OBJECT) ? ((uc_object_t *)obj)->table->head : NULL; \ + (entry##key ? (key = (char *)lh_entry_k(entry##key), \ + val = (uc_value_t *)lh_entry_v(entry##key), \ + entry_next##key = entry##key->next, entry##key) \ + : 0); \ + entry##key = entry_next##key) uc_value_t *ucv_function_new(const char *, size_t, uc_source *); size_t ucv_function_srcpos(uc_value_t *, size_t); diff --git a/util.h b/util.h index a62ccc4..c4ea087 100644 --- a/util.h +++ b/util.h @@ -218,6 +218,7 @@ static inline struct json_tokener *xjs_new_tokener(void) { return tok; } +__attribute__((format(printf, 2, 0))) static inline int xasprintf(char **strp, const char *fmt, ...) { va_list ap; int len; @@ -234,6 +235,7 @@ static inline int xasprintf(char **strp, const char *fmt, ...) { return len; } +__attribute__((format(printf, 2, 0))) static inline int xvasprintf(char **strp, const char *fmt, va_list ap) { int len = vasprintf(strp, fmt, ap); diff --git a/value.c b/value.c index e53e317..0827038 100644 --- a/value.c +++ b/value.c @@ -260,7 +260,7 @@ uc_cmp(int how, uc_value_t *v1, uc_value_t *v2) } else { if (t1 == t2 && !ucv_is_scalar(v1)) { - delta = (void *)v1 - (void *)v2; + delta = (intptr_t)v1 - (intptr_t)v2; } else { t1 = uc_cast_number(v1, &n1, &d1); diff --git a/value.h b/value.h index 770279e..a3abfc6 100644 --- a/value.h +++ b/value.h @@ -29,6 +29,8 @@ #include +#include "types.h" + typedef enum { TAG_INVAL = 0, TAG_NUM = 1, @@ -39,24 +41,11 @@ typedef enum { TAG_PTR = 6 } uc_value_type_t; -typedef struct { - size_t isize; - size_t dsize; - uint64_t *index; - char *data; -} uc_value_list; - -typedef struct uc_value_t uc_value_t; - bool uc_cmp(int how, uc_value_t *v1, uc_value_t *v2); bool uc_val_is_truish(uc_value_t *val); -typedef enum uc_type_t uc_type_t; -typedef struct uc_value_t uc_value_t; uc_type_t uc_cast_number(uc_value_t *v, int64_t *n, double *d); -typedef struct uc_vm uc_vm; - uc_value_t *uc_getval(uc_vm *, uc_value_t *scope, uc_value_t *key); uc_value_t *uc_setval(uc_vm *, uc_value_t *scope, uc_value_t *key, uc_value_t *val); diff --git a/vm.c b/vm.c index 7d60805..ee73265 100644 --- a/vm.c +++ b/vm.c @@ -232,9 +232,9 @@ uc_vm_decode_insn(uc_vm *vm, uc_callframe *frame, uc_chunk *chunk) case -4: vm->arg.s32 = ( - frame->ip[0] * 0x1000000 + - frame->ip[1] * 0x10000 + - frame->ip[2] * 0x100 + + frame->ip[0] * 0x1000000UL + + frame->ip[1] * 0x10000UL + + frame->ip[2] * 0x100UL + frame->ip[3] ) - 0x7fffffff; frame->ip += 4; @@ -247,9 +247,9 @@ uc_vm_decode_insn(uc_vm *vm, uc_callframe *frame, uc_chunk *chunk) case 4: vm->arg.u32 = ( - frame->ip[0] * 0x1000000 + - frame->ip[1] * 0x10000 + - frame->ip[2] * 0x100 + + frame->ip[0] * 0x1000000UL + + frame->ip[1] * 0x10000UL + + frame->ip[2] * 0x100UL + frame->ip[3] ); frame->ip += 4; @@ -307,7 +307,7 @@ uc_vm_frame_dump(uc_vm *vm, uc_callframe *frame) ref = closure->upvals[i]; v = uc_chunk_debug_get_variable(chunk, 0, i, true); s = ucv_to_string(NULL, v); - fprintf(stderr, " [%zu] <%p> %s ", i, ref, s); + fprintf(stderr, " [%zu] <%p> %s ", i, (void *)ref, s); free(s); if (ref->closed) { @@ -588,13 +588,13 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) case -2: fprintf(stderr, " {%c0x%hx}", vm->arg.s16 < 0 ? '-' : '+', - vm->arg.s16 < 0 ? -(unsigned)vm->arg.s16 : (unsigned)vm->arg.s16); + (uint16_t)(vm->arg.s16 < 0 ? -vm->arg.s16 : vm->arg.s16)); break; case -4: fprintf(stderr, " {%c0x%x}", vm->arg.s32 < 0 ? '-' : '+', - vm->arg.s32 < 0 ? -(unsigned)vm->arg.s32 : (unsigned)vm->arg.s32); + (uint32_t)(vm->arg.s32 < 0 ? -vm->arg.s32 : vm->arg.s32)); break; case 1: @@ -1035,7 +1035,7 @@ uc_vm_capture_upval(uc_vm *vm, size_t slot) if (curr && curr->slot == slot) { if (vm->trace) { s = ucv_to_string(NULL, vm->stack.entries[slot]); - fprintf(stderr, " {+%zu} <%p> %s\n", slot, curr, s); + fprintf(stderr, " {+%zu} <%p> %s\n", slot, (void *)curr, s); free(s); } @@ -1047,7 +1047,7 @@ uc_vm_capture_upval(uc_vm *vm, size_t slot) if (vm->trace) { s = ucv_to_string(NULL, vm->stack.entries[slot]); - fprintf(stderr, " {*%zu} <%p> %s\n", slot, created, s); + fprintf(stderr, " {*%zu} <%p> %s\n", slot, (void *)created, s); free(s); } @@ -1072,7 +1072,7 @@ uc_vm_close_upvals(uc_vm *vm, size_t slot) if (vm->trace) { s = ucv_to_string(NULL, ref->value); - fprintf(stderr, " {!%zu} <%p> %s\n", ref->slot, ref, s); + fprintf(stderr, " {!%zu} <%p> %s\n", ref->slot, (void *)ref, s); free(s); } @@ -1528,11 +1528,13 @@ static void uc_vm_insn_sobj(uc_vm *vm, enum insn_type insn) { uc_value_t *obj = uc_vm_stack_peek(vm, vm->arg.u32); + uc_value_t *val; size_t idx; for (idx = 0; idx < vm->arg.u32; idx += 2) { + val = uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1); ucv_object_add(obj, - ucv_string_get(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1)), + ucv_string_get(val), ucv_get(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 2))); } @@ -1972,6 +1974,8 @@ uc_vm_callframe_pop(uc_vm *vm) static void uc_vm_output_exception(uc_vm *vm) { + uc_value_t *ctx; + if (vm->exception.type == EXCEPTION_USER) fprintf(stderr, "%s\n", vm->exception.message); else @@ -1979,10 +1983,9 @@ uc_vm_output_exception(uc_vm *vm) exception_type_strings[vm->exception.type] ? exception_type_strings[vm->exception.type] : "Error", vm->exception.message); - fprintf(stderr, "%s\n\n", - ucv_string_get( - ucv_object_get( - ucv_array_get(vm->exception.stacktrace, 0), "context", NULL))); + ctx = ucv_object_get(ucv_array_get(vm->exception.stacktrace, 0), "context", NULL); + + fprintf(stderr, "%s\n\n", ucv_string_get(ctx)); } static uc_vm_status_t diff --git a/vm.h b/vm.h index e4de4f9..0f7b805 100644 --- a/vm.h +++ b/vm.h @@ -23,6 +23,7 @@ #include "chunk.h" #include "util.h" #include "lexer.h" +#include "types.h" #define __insns \ __insn(NOOP) \ @@ -103,54 +104,6 @@ typedef struct { int8_t operand_bytes; } uc_insn_definition; -typedef enum { - EXCEPTION_NONE, - EXCEPTION_SYNTAX, - EXCEPTION_RUNTIME, - EXCEPTION_TYPE, - EXCEPTION_REFERENCE, - EXCEPTION_USER -} uc_exception_type_t; - -typedef struct { - uc_exception_type_t type; - uc_value_t *stacktrace; - char *message; -} uc_exception; - -typedef struct { - uint8_t *ip; - uc_closure_t *closure; - uc_cfunction_t *cfunction; - size_t stackframe; - uc_value_t *ctx; - bool mcall; -} uc_callframe; - -uc_declare_vector(uc_callframes, uc_callframe); -uc_declare_vector(uc_stack, uc_value_t *); - -typedef struct uc_vm { - uc_stack stack; - uc_exception exception; - uc_callframes callframes; - uc_upvalref_t *open_upvals; - uc_parse_config *config; - uc_value_t *globals; - uc_source *sources; - uc_weakref_t values; - union { - uint32_t u32; - int32_t s32; - uint16_t u16; - int16_t s16; - uint8_t u8; - int8_t s8; - } arg; - size_t spread_values; - uint8_t trace; -} uc_vm; - typedef enum { STATUS_OK, ERROR_COMPILE, -- cgit v1.2.3