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
This commit is contained in:
Ben Noordhuis 2023-12-16 01:01:26 +01:00 committed by GitHub
parent 7474b28036
commit 5cbf8727a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 8 deletions

View file

@ -109,6 +109,7 @@ jobs:
./build/qjs examples/test_fib.js ./build/qjs examples/test_fib.js
./build/qjs examples/test_point.js ./build/qjs examples/test_point.js
./build/qjs tests/test_bjson.js ./build/qjs tests/test_bjson.js
./build/function_source
linux-shared: linux-shared:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -213,6 +214,7 @@ jobs:
./build/qjs examples/test_fib.js ./build/qjs examples/test_fib.js
./build/qjs examples/test_point.js ./build/qjs examples/test_point.js
./build/qjs tests/test_bjson.js ./build/qjs tests/test_bjson.js
./build/function_source
macos-shared: macos-shared:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
@ -261,6 +263,7 @@ jobs:
run: | run: |
cmake -B build -DCMAKE_BUILD_TYPE=${{matrix.buildType}} -G "Visual Studio 17 2022" -T ClangCL 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 qjs_exe
cmake --build build --target function_source
- name: stats - name: stats
run: | run: |
cmd /r build\Debug\qjs.exe -qd 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_std.js
cmd /r build\Debug\qjs.exe tests\test_worker.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\qjs.exe tests\test_queue_microtask.js
cmd /r build\Debug\function_source.exe
windows-mingw: windows-mingw:
runs-on: windows-latest runs-on: windows-latest

View file

@ -236,6 +236,21 @@ add_executable(unicode_gen EXCLUDE_FROM_ALL
) )
target_compile_definitions(unicode_gen PRIVATE ${qjs_defines}) 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 # Examples
# #
@ -246,7 +261,7 @@ if(BUILD_EXAMPLES AND NOT WIN32)
COMMAND qjsc -e -o hello.c ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello.js COMMAND qjsc -e -o hello.c ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello.js
DEPENDS qjsc DEPENDS qjsc
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 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 SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello.js
) )
add_executable(hello 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 COMMAND qjsc -e -o hello_module.c -m ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello_module.js
DEPENDS qjsc DEPENDS qjsc
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 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 SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/examples/hello_module.js
) )
add_executable(hello_module add_executable(hello_module

View file

@ -129,7 +129,7 @@ int dbuf_realloc(DynBuf *s, size_t new_size)
return 0; 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; size_t end;
end = offset + len; end = offset + len;
@ -141,7 +141,7 @@ int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
return 0; 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 (unlikely((s->size + len) > s->allocated_size)) {
if (dbuf_realloc(s, s->size + len)) if (dbuf_realloc(s, s->size + len))

View file

@ -277,8 +277,8 @@ typedef struct DynBuf {
void dbuf_init(DynBuf *s); void dbuf_init(DynBuf *s);
void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
int dbuf_realloc(DynBuf *s, size_t new_size); 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_write(DynBuf *s, size_t offset, const void *data, size_t len);
int dbuf_put(DynBuf *s, const uint8_t *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_put_self(DynBuf *s, size_t offset, size_t len);
int dbuf_putc(DynBuf *s, uint8_t c); int dbuf_putc(DynBuf *s, uint8_t c);
int dbuf_putstr(DynBuf *s, const char *str); int dbuf_putstr(DynBuf *s, const char *str);

1
qjsc.c
View file

@ -305,6 +305,7 @@ static const char main_c_template1[] =
static const char main_c_template2[] = static const char main_c_template2[] =
" js_std_loop(ctx);\n" " js_std_loop(ctx);\n"
" JS_FreeContext(ctx);\n" " JS_FreeContext(ctx);\n"
" js_std_free_handlers(rt);\n"
" JS_FreeRuntime(rt);\n" " JS_FreeRuntime(rt);\n"
" return 0;\n" " return 0;\n"
"}\n"; "}\n";

View file

@ -32018,7 +32018,7 @@ typedef enum BCTagEnum {
BC_TAG_OBJECT_REFERENCE, BC_TAG_OBJECT_REFERENCE,
} BCTagEnum; } BCTagEnum;
#define BC_VERSION 7 #define BC_VERSION 8
typedef struct BCWriterState { typedef struct BCWriterState {
JSContext *ctx; 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->col_num);
bc_put_leb128(s, b->pc2line_len); bc_put_leb128(s, b->pc2line_len);
dbuf_put(&s->dbuf, b->pc2line_buf, 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 */ /* compatibility */
dbuf_putc(&s->dbuf, 255); dbuf_putc(&s->dbuf, 255);
@ -33017,7 +33019,7 @@ static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
return 0; 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 (buf_len != 0) {
if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) 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)) if (bc_get_buf(s, b->pc2line_buf, b->pc2line_len))
goto fail; 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 && if (s->buf_end - s->ptr > 3 && s->ptr[0] == 255 &&
s->ptr[1] == 73 && s->ptr[2] == 67) { s->ptr[1] == 73 && s->ptr[2] == 67) {
s->ptr += 3; s->ptr += 3;

14
tests/function_source.js Normal file
View file

@ -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)
}