From 5cbf8727a62a66a7475c806eab7bbab1e4e4fcdf Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 16 Dec 2023 01:01:26 +0100 Subject: [PATCH] Retain function source code in serialized bytecode (#218) Also fix a small memory leak in the output from `qjsc -e`. Fixes: https://github.com/quickjs-ng/quickjs/issues/217 --- .github/workflows/ci.yml | 4 ++++ CMakeLists.txt | 19 +++++++++++++++++-- cutils.c | 4 ++-- cutils.h | 4 ++-- qjsc.c | 1 + quickjs.c | 15 +++++++++++++-- tests/function_source.js | 14 ++++++++++++++ 7 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 tests/function_source.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aef7fa6..5fb00d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,6 +109,7 @@ jobs: ./build/qjs examples/test_fib.js ./build/qjs examples/test_point.js ./build/qjs tests/test_bjson.js + ./build/function_source linux-shared: runs-on: ubuntu-latest steps: @@ -213,6 +214,7 @@ jobs: ./build/qjs examples/test_fib.js ./build/qjs examples/test_point.js ./build/qjs tests/test_bjson.js + ./build/function_source macos-shared: runs-on: macos-latest steps: @@ -261,6 +263,7 @@ jobs: run: | cmake -B build -DCMAKE_BUILD_TYPE=${{matrix.buildType}} -G "Visual Studio 17 2022" -T ClangCL cmake --build build --target qjs_exe + cmake --build build --target function_source - name: stats run: | cmd /r build\Debug\qjs.exe -qd @@ -274,6 +277,7 @@ jobs: cmd /r build\Debug\qjs.exe tests\test_std.js cmd /r build\Debug\qjs.exe tests\test_worker.js cmd /r build\Debug\qjs.exe tests\test_queue_microtask.js + cmd /r build\Debug\function_source.exe windows-mingw: runs-on: windows-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e4cbc4..2d7eaf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -236,6 +236,21 @@ add_executable(unicode_gen EXCLUDE_FROM_ALL ) target_compile_definitions(unicode_gen PRIVATE ${qjs_defines}) +add_custom_command( + OUTPUT function_source.c + COMMAND qjsc -e -o function_source.c ${CMAKE_CURRENT_SOURCE_DIR}/tests/function_source.js + DEPENDS qjsc ${CMAKE_CURRENT_SOURCE_DIR}/tests/function_source.js + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Compile function_source.js to a C file with bytecode embedded" + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/function_source.js +) +add_executable(function_source + ${CMAKE_CURRENT_BINARY_DIR}/function_source.c + quickjs-libc.c +) +target_include_directories(function_source PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_definitions(function_source PRIVATE ${qjs_defines}) +target_link_libraries(function_source ${qjs_libs}) # Examples # @@ -246,7 +261,7 @@ if(BUILD_EXAMPLES AND NOT WIN32) COMMAND qjsc -e -o hello.c ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello.js DEPENDS qjsc WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Compile hello.js to a C file with bytecode embeddee" + COMMENT "Compile hello.js to a C file with bytecode embedded" SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello.js ) add_executable(hello @@ -262,7 +277,7 @@ if(BUILD_EXAMPLES AND NOT WIN32) COMMAND qjsc -e -o hello_module.c -m ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello_module.js DEPENDS qjsc WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Compile hello_module.js to a C file with bytecode embeddee" + COMMENT "Compile hello_module.js to a C file with bytecode embedded" SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello_module.js ) add_executable(hello_module diff --git a/cutils.c b/cutils.c index 9b1c871..088e75e 100644 --- a/cutils.c +++ b/cutils.c @@ -129,7 +129,7 @@ int dbuf_realloc(DynBuf *s, size_t new_size) return 0; } -int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len) +int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len) { size_t end; end = offset + len; @@ -141,7 +141,7 @@ int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len) return 0; } -int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) +int dbuf_put(DynBuf *s, const void *data, size_t len) { if (unlikely((s->size + len) > s->allocated_size)) { if (dbuf_realloc(s, s->size + len)) diff --git a/cutils.h b/cutils.h index 5cf1b8a..23b3da4 100644 --- a/cutils.h +++ b/cutils.h @@ -277,8 +277,8 @@ typedef struct DynBuf { void dbuf_init(DynBuf *s); void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); int dbuf_realloc(DynBuf *s, size_t new_size); -int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len); -int dbuf_put(DynBuf *s, const uint8_t *data, size_t len); +int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len); +int dbuf_put(DynBuf *s, const void *data, size_t len); int dbuf_put_self(DynBuf *s, size_t offset, size_t len); int dbuf_putc(DynBuf *s, uint8_t c); int dbuf_putstr(DynBuf *s, const char *str); diff --git a/qjsc.c b/qjsc.c index cb584a3..98a4d2b 100644 --- a/qjsc.c +++ b/qjsc.c @@ -305,6 +305,7 @@ static const char main_c_template1[] = static const char main_c_template2[] = " js_std_loop(ctx);\n" " JS_FreeContext(ctx);\n" + " js_std_free_handlers(rt);\n" " JS_FreeRuntime(rt);\n" " return 0;\n" "}\n"; diff --git a/quickjs.c b/quickjs.c index 88f5d8e..c56b963 100644 --- a/quickjs.c +++ b/quickjs.c @@ -32018,7 +32018,7 @@ typedef enum BCTagEnum { BC_TAG_OBJECT_REFERENCE, } BCTagEnum; -#define BC_VERSION 7 +#define BC_VERSION 8 typedef struct BCWriterState { JSContext *ctx; @@ -32440,6 +32440,8 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValue obj) bc_put_leb128(s, b->col_num); bc_put_leb128(s, b->pc2line_len); dbuf_put(&s->dbuf, b->pc2line_buf, b->pc2line_len); + bc_put_leb128(s, b->source_len); + dbuf_put(&s->dbuf, b->source, b->source_len); /* compatibility */ dbuf_putc(&s->dbuf, 255); @@ -33017,7 +33019,7 @@ static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval) return 0; } -static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len) +static int bc_get_buf(BCReaderState *s, void *buf, uint32_t buf_len) { if (buf_len != 0) { if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) @@ -33413,6 +33415,15 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) if (bc_get_buf(s, b->pc2line_buf, b->pc2line_len)) goto fail; } + if (bc_get_leb128_int(s, &b->source_len)) + goto fail; + if (b->source_len) { + b->source = js_mallocz(ctx, b->source_len); + if (!b->source) + goto fail; + if (bc_get_buf(s, b->source, b->source_len)) + goto fail; + } if (s->buf_end - s->ptr > 3 && s->ptr[0] == 255 && s->ptr[1] == 73 && s->ptr[2] == 67) { s->ptr += 3; diff --git a/tests/function_source.js b/tests/function_source.js new file mode 100644 index 0000000..640420a --- /dev/null +++ b/tests/function_source.js @@ -0,0 +1,14 @@ +"use strict" +const expect = "function f() { return 42 }" +function f() { return 42 } + +{ + const actual = f.toString() + if (actual !== expect) throw Error(actual) +} + +{ + const f = eval(expect + "f") + const actual = f.toString() + if (actual !== expect) throw Error(actual) +}