- removed the JS_EVAL_FLAG_STRIP eval flag and replaced it with JS_SetStripInfo() which has simpler semantics.
- qjs: added the '-s' and '--strip-source' options
- qjsc: added the '-s' and '--keep-source' options
$(AR) rcs $@ $^
repl.c: $(QJSC) repl.js
- $(QJSC) -c -o $@ -m repl.js
+ $(QJSC) -s -c -o $@ -m repl.js
ifneq ($(wildcard unicode/UnicodeData.txt),)
$(OBJDIR)/libunicode.o $(OBJDIR)/libunicode.nolto.o: libunicode-table.h
ECMA402 (Internationalization API) is not supported.
-@subsection Extensions
-
-@itemize
-
-@item The directive @code{"use strip"} indicates that the debug information (including the source code of the functions) should not be retained to save memory. As @code{"use strict"}, the directive can be global to a script or local to a function.
-
-@item The first line of a script beginning with @code{#!} is ignored.
-
-@end itemize
-
@section Modules
ES6 modules are fully supported. The default name resolution is the
" --memory-limit n limit the memory usage to 'n' bytes (SI suffixes allowed)\n"
" --stack-size n limit the stack size to 'n' bytes (SI suffixes allowed)\n"
" --no-unhandled-rejection ignore unhandled promise rejections\n"
+ "-s strip all the debug info\n"
+ " --strip-source strip the source code\n"
"-q --quit just instantiate the interpreter and quit\n");
exit(1);
}
size_t memory_limit = 0;
char *include_list[32];
int i, include_count = 0;
+ int strip_flags = 0;
size_t stack_size = 0;
/* cannot use getopt because we want to pass the command line to
stack_size = get_suffixed_size(argv[optind++]);
continue;
}
+ if (opt == 's') {
+ strip_flags = JS_STRIP_DEBUG;
+ continue;
+ }
+ if (!strcmp(longopt, "strip-source")) {
+ strip_flags = JS_STRIP_SOURCE;
+ continue;
+ }
if (opt) {
fprintf(stderr, "qjs: unknown option '-%c'\n", opt);
} else {
JS_SetMemoryLimit(rt, memory_limit);
if (stack_size != 0)
JS_SetMaxStackSize(rt, stack_size);
+ JS_SetStripInfo(rt, strip_flags);
js_std_set_worker_new_context_func(JS_NewCustomContext);
js_std_init_handlers(rt);
ctx = JS_NewCustomContext(rt);
"-M module_name[,cname] add initialization code for an external C module\n"
"-x byte swapped output\n"
"-p prefix set the prefix of the generated C names\n"
- "-S n set the maximum stack size to 'n' bytes (default=%d)\n",
+ "-S n set the maximum stack size to 'n' bytes (default=%d)\n"
+ "-s strip all the debug info\n"
+ "--keep-source keep the source code\n",
JS_DEFAULT_STACK_SIZE);
#ifdef CONFIG_LTO
{
}
#endif
+static size_t get_suffixed_size(const char *str)
+{
+ char *p;
+ size_t v;
+ v = (size_t)strtod(str, &p);
+ switch(*p) {
+ case 'G':
+ v <<= 30;
+ break;
+ case 'M':
+ v <<= 20;
+ break;
+ case 'k':
+ case 'K':
+ v <<= 10;
+ break;
+ default:
+ if (*p != '\0') {
+ fprintf(stderr, "qjs: invalid suffix: %s\n", p);
+ exit(1);
+ }
+ break;
+ }
+ return v;
+}
typedef enum {
OUTPUT_C,
OUTPUT_EXECUTABLE,
} OutputTypeEnum;
+static const char *get_short_optarg(int *poptind, int opt,
+ const char *arg, int argc, char **argv)
+{
+ const char *optarg;
+ if (*arg) {
+ optarg = arg;
+ } else if (*poptind < argc) {
+ optarg = argv[(*poptind)++];
+ } else {
+ fprintf(stderr, "qjsc: expecting parameter for -%c\n", opt);
+ exit(1);
+ }
+ return optarg;
+}
+
int main(int argc, char **argv)
{
- int c, i, verbose;
+ int i, verbose, strip_flags;
const char *out_filename, *cname;
char cfilename[1024];
FILE *fo;
module = -1;
byte_swap = FALSE;
verbose = 0;
+ strip_flags = JS_STRIP_SOURCE;
use_lto = FALSE;
stack_size = 0;
memset(&dynamic_module_list, 0, sizeof(dynamic_module_list));
namelist_add(&cmodule_list, "std", "std", 0);
namelist_add(&cmodule_list, "os", "os", 0);
- for(;;) {
- c = getopt(argc, argv, "ho:cN:f:mxevM:p:S:D:");
- if (c == -1)
- break;
- switch(c) {
- case 'h':
- help();
- case 'o':
- out_filename = optarg;
- break;
- case 'c':
- output_type = OUTPUT_C;
- break;
- case 'e':
- output_type = OUTPUT_C_MAIN;
- break;
- case 'N':
- cname = optarg;
+ optind = 1;
+ while (optind < argc && *argv[optind] == '-') {
+ char *arg = argv[optind] + 1;
+ const char *longopt = "";
+ const char *optarg;
+ /* a single - is not an option, it also stops argument scanning */
+ if (!*arg)
break;
- case 'f':
- {
+ optind++;
+ if (*arg == '-') {
+ longopt = arg + 1;
+ arg += strlen(arg);
+ /* -- stops argument scanning */
+ if (!*longopt)
+ break;
+ }
+ for (; *arg || *longopt; longopt = "") {
+ char opt = *arg;
+ if (opt)
+ arg++;
+ if (opt == 'h' || opt == '?' || !strcmp(longopt, "help")) {
+ help();
+ continue;
+ }
+ if (opt == 'o') {
+ out_filename = get_short_optarg(&optind, opt, arg, argc, argv);
+ break;
+ }
+ if (opt == 'c') {
+ output_type = OUTPUT_C;
+ continue;
+ }
+ if (opt == 'e') {
+ output_type = OUTPUT_C_MAIN;
+ continue;
+ }
+ if (opt == 'N') {
+ cname = get_short_optarg(&optind, opt, arg, argc, argv);
+ break;
+ }
+ if (opt == 'f') {
const char *p;
+ optarg = get_short_optarg(&optind, opt, arg, argc, argv);
p = optarg;
- if (!strcmp(optarg, "lto")) {
+ if (!strcmp(p, "lto")) {
use_lto = TRUE;
} else if (strstart(p, "no-", &p)) {
use_lto = TRUE;
fprintf(stderr, "unsupported feature: %s\n", optarg);
exit(1);
}
+ break;
}
- break;
- case 'm':
- module = 1;
- break;
- case 'M':
- {
+ if (opt == 'm') {
+ module = 1;
+ continue;
+ }
+ if (opt == 'M') {
char *p;
char path[1024];
char cname[1024];
+
+ optarg = get_short_optarg(&optind, opt, arg, argc, argv);
pstrcpy(path, sizeof(path), optarg);
p = strchr(path, ',');
if (p) {
get_c_name(cname, sizeof(cname), path);
}
namelist_add(&cmodule_list, path, cname, 0);
+ break;
}
- break;
- case 'D':
- namelist_add(&dynamic_module_list, optarg, NULL, 0);
- break;
- case 'x':
- byte_swap = TRUE;
- break;
- case 'v':
- verbose++;
- break;
- case 'p':
- c_ident_prefix = optarg;
- break;
- case 'S':
- stack_size = (size_t)strtod(optarg, NULL);
- break;
- default:
- break;
+ if (opt == 'D') {
+ optarg = get_short_optarg(&optind, opt, arg, argc, argv);
+ namelist_add(&dynamic_module_list, optarg, NULL, 0);
+ break;
+ }
+ if (opt == 'x') {
+ byte_swap = 1;
+ continue;
+ }
+ if (opt == 'v') {
+ verbose++;
+ continue;
+ }
+ if (opt == 'p') {
+ c_ident_prefix = get_short_optarg(&optind, opt, arg, argc, argv);
+ break;
+ }
+ if (opt == 'S') {
+ optarg = get_short_optarg(&optind, opt, arg, argc, argv);
+ stack_size = get_suffixed_size(optarg);
+ break;
+ }
+ if (opt == 's') {
+ strip_flags = JS_STRIP_DEBUG;
+ continue;
+ }
+ if (!strcmp(longopt, "keep-source")) {
+ strip_flags = 0;
+ continue;
+ }
+ if (opt) {
+ fprintf(stderr, "qjsc: unknown option '-%c'\n", opt);
+ } else {
+ fprintf(stderr, "qjsc: unknown option '--%s'\n", longopt);
+ }
+ help();
}
}
rt = JS_NewRuntime();
ctx = JS_NewContext(rt);
+ JS_SetStripInfo(rt, strip_flags);
+
/* loader for ES6 modules */
JS_SetModuleLoaderFunc(rt, NULL, jsc_module_loader, NULL);
char *filename; /* module filename */
char *basename; /* module base name */
JSWorkerMessagePipe *recv_pipe, *send_pipe;
+ int strip_flags;
} WorkerFuncArgs;
typedef struct {
fprintf(stderr, "JS_NewRuntime failure");
exit(1);
}
+ JS_SetStripInfo(rt, args->strip_flags);
js_std_init_handlers(rt);
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
if (!args->send_pipe)
goto oom_fail;
+ args->strip_flags = JS_GetStripInfo(rt);
+
obj = js_worker_ctor_internal(ctx, new_target,
args->send_pipe, args->recv_pipe);
if (JS_IsException(obj))
BOOL can_block : 8; /* TRUE if Atomics.wait can block */
/* used to allocate, free and clone SharedArrayBuffers */
JSSharedArrayBufferFunctions sab_funcs;
-
+ /* see JS_SetStripInfo() */
+ uint8_t strip_flags;
+
/* Shape hash table */
int shape_hash_bits;
int shape_hash_size;
};
#define JS_MODE_STRICT (1 << 0)
-#define JS_MODE_STRIP (1 << 1)
#define JS_MODE_ASYNC (1 << 2) /* async function */
#define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */
/* debug info, move to separate structure to save memory? */
JSAtom filename;
int line_num;
- int source_len;
int pc2line_len;
uint8_t *pc2line_buf;
+ int source_len;
char *source;
} debug;
} JSFunctionBytecode;
rt->sab_funcs = *sf;
}
+void JS_SetStripInfo(JSRuntime *rt, int flags)
+{
+ rt->strip_flags = flags;
+}
+
+int JS_GetStripInfo(JSRuntime *rt)
+{
+ return rt->strip_flags;
+}
+
/* return 0 if OK, < 0 if exception */
int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
int argc, JSValueConst *argv)
int line_number_last_pc;
/* pc2line table */
+ BOOL strip_debug : 1; /* strip all debug info (implies strip_source = TRUE) */
+ BOOL strip_source : 1; /* strip only source code */
JSAtom filename;
int line_num;
DynBuf pc2line;
put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
/* store the class source code in the constructor. */
- if (!(fd->js_mode & JS_MODE_STRIP)) {
+ if (!fd->strip_source) {
js_free(ctx, ctor_fd->source);
ctor_fd->source_len = s->buf_ptr - class_start_ptr;
ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
fd->js_mode = parent->js_mode;
fd->parent_scope_level = parent->scope_level;
}
+ fd->strip_debug = ((ctx->rt->strip_flags & JS_STRIP_DEBUG) != 0);
+ fd->strip_source = ((ctx->rt->strip_flags & (JS_STRIP_DEBUG | JS_STRIP_SOURCE)) != 0);
fd->is_eval = is_eval;
fd->is_func_expr = is_func_expr;
static void compute_pc2line_info(JSFunctionDef *s)
{
- if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {
+ if (!s->strip_debug && s->line_number_slots) {
int last_line_num = s->line_num;
uint32_t last_pc = 0;
int i;
}
#endif
/* XXX: Should skip this phase if not generating SHORT_OPCODES */
- if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {
+ if (s->line_number_size && !s->strip_debug) {
s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
if (s->line_number_slots == NULL)
return -1;
}
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
- if (!(fd->js_mode & JS_MODE_STRIP)) {
+ if (!s->strip_debug) {
printf("pass 1\n");
dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
fd->args, fd->arg_count, fd->vars, fd->var_count,
goto fail;
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
- if (!(fd->js_mode & JS_MODE_STRIP)) {
+ if (!s->strip_debug) {
printf("pass 2\n");
dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
fd->args, fd->arg_count, fd->vars, fd->var_count,
if (compute_stack_size(ctx, fd, &stack_size) < 0)
goto fail;
- if (fd->js_mode & JS_MODE_STRIP) {
+ if (fd->strip_debug) {
function_size = offsetof(JSFunctionBytecode, debug);
} else {
function_size = sizeof(*b);
cpool_offset = function_size;
function_size += fd->cpool_count * sizeof(*fd->cpool);
vardefs_offset = function_size;
- if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {
+ if (!fd->strip_debug || fd->has_eval_call) {
function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
}
closure_var_offset = function_size;
b->func_name = fd->func_name;
if (fd->arg_count + fd->var_count > 0) {
- if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {
+ if (fd->strip_debug && !fd->has_eval_call) {
/* Strip variable definitions not needed at runtime */
int i;
for(i = 0; i < fd->var_count; i++) {
b->stack_size = stack_size;
- if (fd->js_mode & JS_MODE_STRIP) {
+ if (fd->strip_debug) {
JS_FreeAtom(ctx, fd->filename);
dbuf_free(&fd->pc2line); // probably useless
} else {
add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
- if (!(fd->js_mode & JS_MODE_STRIP)) {
+ if (!s->strip_debug) {
js_dump_function_bytecode(ctx, b);
}
#endif
s->cur_func->has_use_strict = TRUE;
s->cur_func->js_mode |= JS_MODE_STRICT;
}
-#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)
- else if (!strcmp(str, "use strip")) {
- s->cur_func->js_mode |= JS_MODE_STRIP;
- }
-#endif
}
return js_parse_seek_token(s, &pos);
}
else
emit_op(s, OP_return);
- if (!(fd->js_mode & JS_MODE_STRIP)) {
+ if (!fd->strip_source) {
/* save the function source code */
/* the end of the function source code is after the last
token of the function source stored into s->last_ptr */
if (js_parse_source_element(s))
goto fail;
}
- if (!(fd->js_mode & JS_MODE_STRIP)) {
+ if (!fd->strip_source) {
/* save the function source code */
fd->source_len = s->buf_ptr - ptr;
fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
js_mode = 0;
if (flags & JS_EVAL_FLAG_STRICT)
js_mode |= JS_MODE_STRICT;
- if (flags & JS_EVAL_FLAG_STRIP)
- js_mode |= JS_MODE_STRIP;
if (eval_type == JS_EVAL_TYPE_MODULE) {
JSAtom module_name = JS_NewAtom(ctx, filename);
if (module_name == JS_ATOM_NULL)
bc_put_leb128(s, b->debug.line_num);
bc_put_leb128(s, b->debug.pc2line_len);
dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
+ if (b->debug.source) {
+ bc_put_leb128(s, b->debug.source_len);
+ dbuf_put(&s->dbuf, (uint8_t *)b->debug.source, b->debug.source_len);
+ } else {
+ bc_put_leb128(s, 0);
+ }
}
for(i = 0; i < b->cpool_count; i++) {
goto fail;
if (bc_get_leb128_int(s, &b->debug.line_num))
goto fail;
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf(" line: %d\n", b->debug.line_num);
+#endif
if (bc_get_leb128_int(s, &b->debug.pc2line_len))
goto fail;
if (b->debug.pc2line_len) {
if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
goto fail;
}
-#ifdef DUMP_READ_OBJECT
- bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
-#endif
+ if (bc_get_leb128_int(s, &b->debug.source_len))
+ goto fail;
+ if (b->debug.source_len) {
+ bc_read_trace(s, "source: %d bytes\n", b->source_len);
+ b->debug.source = js_mallocz(ctx, b->debug.source_len);
+ if (!b->debug.source)
+ goto fail;
+ if (bc_get_buf(s, (uint8_t *)b->debug.source, b->debug.source_len))
+ goto fail;
+ }
bc_read_trace(s, "}\n");
}
if (b->cpool_count != 0) {
#define JS_EVAL_TYPE_MASK (3 << 0)
#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */
-#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */
/* compile but do not run. The result is an object with a
JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
with JS_EvalFunction(). */
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
/* if can_block is TRUE, Atomics.wait() can be used */
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
+/* select which debug info is stripped from the compiled code */
+#define JS_STRIP_SOURCE (1 << 0) /* strip source code */
+#define JS_STRIP_DEBUG (1 << 1) /* strip all debug info including source code */
+void JS_SetStripInfo(JSRuntime *rt, int flags);
+int JS_GetStripInfo(JSRuntime *rt);
+
/* set the [IsHTMLDDA] internal slot */
void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj);
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-"use strip";
-
import * as std from "std";
import * as os from "os";
for (i = 0; i < ip->count; i++) {
if (eval_file(ctx, harness, ip->array[i],
- JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRIP)) {
+ JS_EVAL_TYPE_GLOBAL)) {
fatal(1, "error including %s for %s", ip->array[i], filename);
}
}