JavaScript-QuickJS
view release on metacpan or search on metacpan
quickjs/quickjs.c view on Meta::CPAN
}
}
JS_FreeAtom(ctx, var_name);
if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
break_entry.has_iterator = is_for_of = TRUE;
break_entry.drop_count += 2;
if (has_initializer)
goto initializer_error;
} else if (s->token.val == TOK_IN) {
if (is_async)
return js_parse_error(s, "'for await' loop should be used with 'of'");
if (has_initializer &&
(tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
has_destructuring)) {
initializer_error:
return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
is_for_of ? "of" : "in");
}
} else {
return js_parse_error(s, "expected 'of' or 'in' in for control expression");
}
if (next_token(s))
return -1;
if (is_for_of) {
if (js_parse_assign_expr(s))
return -1;
} else {
if (js_parse_expr(s))
return -1;
}
/* close the scope after having evaluated the expression so that
the TDZ values are in the closures */
close_scopes(s, s->cur_func->scope_level, block_scope_level);
if (is_for_of) {
if (is_async)
emit_op(s, OP_for_await_of_start);
else
emit_op(s, OP_for_of_start);
/* on stack: enum_rec */
} else {
emit_op(s, OP_for_in_start);
/* on stack: enum_obj */
}
emit_goto(s, OP_goto, label_cont);
if (js_parse_expect(s, ')'))
return -1;
if (OPTIMIZE) {
/* move the `next` code here */
DynBuf *bc = &s->cur_func->byte_code;
int chunk_size = pos_expr - pos_next;
int offset = bc->size - pos_next;
int i;
dbuf_realloc(bc, bc->size + chunk_size);
dbuf_put(bc, bc->buf + pos_next, chunk_size);
memset(bc->buf + pos_next, OP_nop, chunk_size);
/* `next` part ends with a goto */
s->cur_func->last_opcode_pos = bc->size - 5;
/* relocate labels */
for (i = label_cont; i < s->cur_func->label_count; i++) {
LabelSlot *ls = &s->cur_func->label_slots[i];
if (ls->pos >= pos_next && ls->pos < pos_expr)
ls->pos += offset;
}
}
emit_label(s, label_body);
if (js_parse_statement(s))
return -1;
close_scopes(s, s->cur_func->scope_level, block_scope_level);
emit_label(s, label_cont);
if (is_for_of) {
if (is_async) {
/* call the next method */
/* stack: iter_obj next catch_offset */
emit_op(s, OP_dup3);
emit_op(s, OP_drop);
emit_op(s, OP_call_method);
emit_u16(s, 0);
/* get the result of the promise */
emit_op(s, OP_await);
/* unwrap the value and done values */
emit_op(s, OP_iterator_get_value_done);
} else {
emit_op(s, OP_for_of_next);
emit_u8(s, 0);
}
} else {
emit_op(s, OP_for_in_next);
}
/* on stack: enum_rec / enum_obj value bool */
emit_goto(s, OP_if_false, label_next);
/* drop the undefined value from for_xx_next */
emit_op(s, OP_drop);
emit_label(s, label_break);
if (is_for_of) {
/* close and drop enum_rec */
emit_op(s, OP_iterator_close);
} else {
emit_op(s, OP_drop);
}
pop_break_entry(s->cur_func);
pop_scope(s);
return 0;
}
static void set_eval_ret_undefined(JSParseState *s)
{
if (s->cur_func->eval_ret_idx >= 0) {
emit_op(s, OP_undefined);
emit_op(s, OP_put_loc);
emit_u16(s, s->cur_func->eval_ret_idx);
}
}
static __exception int js_parse_statement_or_decl(JSParseState *s,
quickjs/quickjs.c view on Meta::CPAN
label_test = new_label(s);
label_cont = new_label(s);
label_body = new_label(s);
label_break = new_label(s);
push_break_entry(s->cur_func, &break_entry,
label_name, label_break, label_cont, 0);
/* test expression */
if (s->token.val == ';') {
/* no test expression */
label_test = label_body;
} else {
emit_label(s, label_test);
if (js_parse_expr(s))
goto fail;
emit_goto(s, OP_if_false, label_break);
}
if (js_parse_expect(s, ';'))
goto fail;
if (s->token.val == ')') {
/* no end expression */
break_entry.label_cont = label_cont = label_test;
pos_cont = 0; /* avoid warning */
} else {
/* skip the end expression */
emit_goto(s, OP_goto, label_body);
pos_cont = s->cur_func->byte_code.size;
emit_label(s, label_cont);
if (js_parse_expr(s))
goto fail;
emit_op(s, OP_drop);
if (label_test != label_body)
emit_goto(s, OP_goto, label_test);
}
if (js_parse_expect(s, ')'))
goto fail;
pos_body = s->cur_func->byte_code.size;
emit_label(s, label_body);
if (js_parse_statement(s))
goto fail;
/* close the closures before the next iteration */
/* XXX: check continue case */
close_scopes(s, s->cur_func->scope_level, block_scope_level);
if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
/* move the increment code here */
DynBuf *bc = &s->cur_func->byte_code;
int chunk_size = pos_body - pos_cont;
int offset = bc->size - pos_cont;
int i;
dbuf_realloc(bc, bc->size + chunk_size);
dbuf_put(bc, bc->buf + pos_cont, chunk_size);
memset(bc->buf + pos_cont, OP_nop, chunk_size);
/* increment part ends with a goto */
s->cur_func->last_opcode_pos = bc->size - 5;
/* relocate labels */
for (i = label_cont; i < s->cur_func->label_count; i++) {
LabelSlot *ls = &s->cur_func->label_slots[i];
if (ls->pos >= pos_cont && ls->pos < pos_body)
ls->pos += offset;
}
} else {
emit_goto(s, OP_goto, label_cont);
}
emit_label(s, label_break);
pop_break_entry(s->cur_func);
pop_scope(s);
}
break;
case TOK_BREAK:
case TOK_CONTINUE:
{
int is_cont = s->token.val - TOK_BREAK;
int label;
if (next_token(s))
goto fail;
if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
label = s->token.u.ident.atom;
else
label = JS_ATOM_NULL;
if (emit_break(s, label, is_cont))
goto fail;
if (label != JS_ATOM_NULL) {
if (next_token(s))
goto fail;
}
if (js_parse_expect_semi(s))
goto fail;
}
break;
case TOK_SWITCH:
{
int label_case, label_break, label1;
int default_label_pos;
BlockEnv break_entry;
if (next_token(s))
goto fail;
set_eval_ret_undefined(s);
if (js_parse_expr_paren(s))
goto fail;
push_scope(s);
label_break = new_label(s);
push_break_entry(s->cur_func, &break_entry,
label_name, label_break, -1, 1);
if (js_parse_expect(s, '{'))
goto fail;
default_label_pos = -1;
label_case = -1;
quickjs/quickjs.c view on Meta::CPAN
if (hf->cpool_idx >= 0 || force_init) {
if (hf->cpool_idx >= 0) {
dbuf_putc(bc, OP_fclosure);
dbuf_put_u32(bc, hf->cpool_idx);
if (hf->var_name == JS_ATOM__default_) {
/* set default export function name */
dbuf_putc(bc, OP_set_name);
dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
}
} else {
dbuf_putc(bc, OP_undefined);
}
if (has_closure == 2) {
dbuf_putc(bc, OP_put_var_ref);
dbuf_put_u16(bc, idx);
} else if (has_closure == 1) {
dbuf_putc(bc, OP_define_field);
dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
dbuf_putc(bc, OP_drop);
} else {
/* XXX: Check if variable is writable and enumerable */
dbuf_putc(bc, OP_put_var);
dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
}
}
done_global_var:
JS_FreeAtom(ctx, hf->var_name);
}
if (s->module) {
dbuf_putc(bc, OP_return_undef);
dbuf_putc(bc, OP_label);
dbuf_put_u32(bc, label_next);
s->label_slots[label_next].pos2 = bc->size;
}
js_free(ctx, s->global_vars);
s->global_vars = NULL;
s->global_var_count = 0;
s->global_var_size = 0;
}
static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
int pos, int *linep)
{
int op, len, label;
for (; pos < bc_len; pos += len) {
op = bc_buf[pos];
len = opcode_info[op].size;
if (op == OP_line_num) {
*linep = get_u32(bc_buf + pos + 1);
} else
if (op == OP_label) {
label = get_u32(bc_buf + pos + 1);
if (update_label(s, label, 0) > 0)
break;
#if 0
if (s->label_slots[label].first_reloc) {
printf("line %d: unreferenced label %d:%d has relocations\n",
*linep, label, s->label_slots[label].pos2);
}
#endif
assert(s->label_slots[label].first_reloc == NULL);
} else {
/* XXX: output a warning for unreachable code? */
JSAtom atom;
switch(opcode_info[op].fmt) {
case OP_FMT_label:
case OP_FMT_label_u16:
label = get_u32(bc_buf + pos + 1);
update_label(s, label, -1);
break;
case OP_FMT_atom_label_u8:
case OP_FMT_atom_label_u16:
label = get_u32(bc_buf + pos + 5);
update_label(s, label, -1);
/* fall thru */
case OP_FMT_atom:
case OP_FMT_atom_u8:
case OP_FMT_atom_u16:
atom = get_u32(bc_buf + pos + 1);
JS_FreeAtom(s->ctx, atom);
break;
default:
break;
}
}
}
return pos;
}
static int get_label_pos(JSFunctionDef *s, int label)
{
int i, pos;
for (i = 0; i < 20; i++) {
pos = s->label_slots[label].pos;
for (;;) {
switch (s->byte_code.buf[pos]) {
case OP_line_num:
case OP_label:
pos += 5;
continue;
case OP_goto:
label = get_u32(s->byte_code.buf + pos + 1);
break;
default:
return pos;
}
break;
}
}
return pos;
}
/* convert global variable accesses to local variables or closure
variables when necessary */
static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
{
int pos, pos_next, bc_len, op, len, i, idx, line_num;
quickjs/quickjs.c view on Meta::CPAN
if (s->this_var_idx >= 0) {
if (s->is_derived_class_constructor) {
dbuf_putc(&bc_out, OP_set_loc_uninitialized);
dbuf_put_u16(&bc_out, s->this_var_idx);
} else {
dbuf_putc(&bc_out, OP_push_this);
put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
}
}
/* initialize the 'arguments' variable if needed */
if (s->arguments_var_idx >= 0) {
if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
dbuf_putc(&bc_out, OP_special_object);
dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
} else {
dbuf_putc(&bc_out, OP_special_object);
dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
}
if (s->arguments_arg_idx >= 0)
put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
}
/* initialize a reference to the current function if needed */
if (s->func_var_idx >= 0) {
dbuf_putc(&bc_out, OP_special_object);
dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
}
/* initialize the variable environment object if needed */
if (s->var_object_idx >= 0) {
dbuf_putc(&bc_out, OP_special_object);
dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
}
if (s->arg_var_object_idx >= 0) {
dbuf_putc(&bc_out, OP_special_object);
dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
}
for (pos = 0; pos < bc_len; pos = pos_next) {
int val;
op = bc_buf[pos];
len = opcode_info[op].size;
pos_next = pos + len;
switch(op) {
case OP_line_num:
/* line number info (for debug). We put it in a separate
compressed table to reduce memory usage and get better
performance */
line_num = get_u32(bc_buf + pos + 1);
break;
case OP_label:
{
label = get_u32(bc_buf + pos + 1);
assert(label >= 0 && label < s->label_count);
ls = &label_slots[label];
assert(ls->addr == -1);
ls->addr = bc_out.size;
/* resolve the relocation entries */
for(re = ls->first_reloc; re != NULL; re = re_next) {
int diff = ls->addr - re->addr;
re_next = re->next;
switch (re->size) {
case 4:
put_u32(bc_out.buf + re->addr, diff);
break;
case 2:
assert(diff == (int16_t)diff);
put_u16(bc_out.buf + re->addr, diff);
break;
case 1:
assert(diff == (int8_t)diff);
put_u8(bc_out.buf + re->addr, diff);
break;
}
js_free(ctx, re);
}
ls->first_reloc = NULL;
}
break;
case OP_call:
case OP_call_method:
{
/* detect and transform tail calls */
int argc;
argc = get_u16(bc_buf + pos + 1);
if (code_match(&cc, pos_next, OP_return, -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
add_pc2line_info(s, bc_out.size, line_num);
put_short_code(&bc_out, op + 1, argc);
pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
break;
}
add_pc2line_info(s, bc_out.size, line_num);
put_short_code(&bc_out, op, argc);
break;
}
goto no_change;
case OP_return:
case OP_return_undef:
case OP_return_async:
case OP_throw:
case OP_throw_error:
pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
goto no_change;
case OP_goto:
label = get_u32(bc_buf + pos + 1);
has_goto:
if (OPTIMIZE) {
int line1 = -1;
/* Use custom matcher because multiple labels can follow */
label = find_jump_target(s, label, &op1, &line1);
if (code_has_label(&cc, pos_next, label)) {
/* jump to next instruction: remove jump */
update_label(s, label, -1);
break;
quickjs/quickjs.c view on Meta::CPAN
label = cc.label;
op ^= OP_if_true ^ OP_if_false;
}
}
}
has_label:
add_pc2line_info(s, bc_out.size, line_num);
if (op == OP_goto) {
pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
}
assert(label >= 0 && label < s->label_count);
ls = &label_slots[label];
#if SHORT_OPCODES
jp = &s->jump_slots[s->jump_count++];
jp->op = op;
jp->size = 4;
jp->pos = bc_out.size + 1;
jp->label = label;
if (ls->addr == -1) {
int diff = ls->pos2 - pos - 1;
if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
jp->size = 1;
jp->op = OP_if_false8 + (op - OP_if_false);
dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
dbuf_putc(&bc_out, 0);
if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
goto fail;
break;
}
if (diff < 32768 && op == OP_goto) {
jp->size = 2;
jp->op = OP_goto16;
dbuf_putc(&bc_out, OP_goto16);
dbuf_put_u16(&bc_out, 0);
if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
goto fail;
break;
}
} else {
int diff = ls->addr - bc_out.size - 1;
if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
jp->size = 1;
jp->op = OP_if_false8 + (op - OP_if_false);
dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
dbuf_putc(&bc_out, diff);
break;
}
if (diff == (int16_t)diff && op == OP_goto) {
jp->size = 2;
jp->op = OP_goto16;
dbuf_putc(&bc_out, OP_goto16);
dbuf_put_u16(&bc_out, diff);
break;
}
}
#endif
dbuf_putc(&bc_out, op);
dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
if (ls->addr == -1) {
/* unresolved yet: create a new relocation entry */
if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
goto fail;
}
break;
case OP_with_get_var:
case OP_with_put_var:
case OP_with_delete_var:
case OP_with_make_ref:
case OP_with_get_ref:
case OP_with_get_ref_undef:
{
JSAtom atom;
int is_with;
atom = get_u32(bc_buf + pos + 1);
label = get_u32(bc_buf + pos + 5);
is_with = bc_buf[pos + 9];
if (OPTIMIZE) {
label = find_jump_target(s, label, &op1, NULL);
}
assert(label >= 0 && label < s->label_count);
ls = &label_slots[label];
add_pc2line_info(s, bc_out.size, line_num);
#if SHORT_OPCODES
jp = &s->jump_slots[s->jump_count++];
jp->op = op;
jp->size = 4;
jp->pos = bc_out.size + 5;
jp->label = label;
#endif
dbuf_putc(&bc_out, op);
dbuf_put_u32(&bc_out, atom);
dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
if (ls->addr == -1) {
/* unresolved yet: create a new relocation entry */
if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
goto fail;
}
dbuf_putc(&bc_out, is_with);
}
break;
case OP_drop:
if (OPTIMIZE) {
/* remove useless drops before return */
if (code_match(&cc, pos_next, OP_return_undef, -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
break;
}
}
goto no_change;
case OP_null:
#if SHORT_OPCODES
if (OPTIMIZE) {
/* transform null strict_eq into is_null */
if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
add_pc2line_info(s, bc_out.size, line_num);
dbuf_putc(&bc_out, OP_is_null);
pos_next = cc.pos;
break;
}
/* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
if (cc.line_num >= 0) line_num = cc.line_num;
add_pc2line_info(s, bc_out.size, line_num);
dbuf_putc(&bc_out, OP_is_null);
pos_next = cc.pos;
label = cc.label;
op = cc.op ^ OP_if_false ^ OP_if_true;
goto has_label;
}
}
#endif
/* fall thru */
case OP_push_false:
case OP_push_true:
if (OPTIMIZE) {
val = (op == OP_push_true);
if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
has_constant_test:
if (cc.line_num >= 0) line_num = cc.line_num;
if (val == cc.op - OP_if_false) {
/* transform null if_false(l1) -> goto l1 */
/* transform false if_false(l1) -> goto l1 */
/* transform true if_true(l1) -> goto l1 */
pos_next = cc.pos;
op = OP_goto;
label = cc.label;
goto has_goto;
} else {
/* transform null if_true(l1) -> nop */
/* transform false if_true(l1) -> nop */
/* transform true if_false(l1) -> nop */
quickjs/quickjs.c view on Meta::CPAN
break;
case BC_TAG_OBJECT_REFERENCE:
{
uint32_t val;
if (!s->allow_reference)
return JS_ThrowSyntaxError(ctx, "object references are not allowed");
if (bc_get_leb128(s, &val))
return JS_EXCEPTION;
bc_read_trace(s, "%u\n", val);
if (val >= s->objects_count) {
return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
val, s->objects_count);
}
obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
}
break;
default:
invalid_tag:
return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
tag, (unsigned int)(s->ptr - s->buf_start));
}
bc_read_trace(s, "}\n");
return obj;
}
static int JS_ReadObjectAtoms(BCReaderState *s)
{
uint8_t v8;
JSString *p;
int i;
JSAtom atom;
if (bc_get_u8(s, &v8))
return -1;
/* XXX: could support byte swapped input */
if (v8 != BC_VERSION) {
JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
v8, BC_VERSION);
return -1;
}
if (bc_get_leb128(s, &s->idx_to_atom_count))
return -1;
bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
if (s->idx_to_atom_count != 0) {
s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
sizeof(s->idx_to_atom[0]));
if (!s->idx_to_atom)
return s->error_state = -1;
}
for(i = 0; i < s->idx_to_atom_count; i++) {
p = JS_ReadString(s);
if (!p)
return -1;
atom = JS_NewAtomStr(s->ctx, p);
if (atom == JS_ATOM_NULL)
return s->error_state = -1;
s->idx_to_atom[i] = atom;
if (s->is_rom_data && (atom != (i + s->first_atom)))
s->is_rom_data = FALSE; /* atoms must be relocated */
}
bc_read_trace(s, "}\n");
return 0;
}
static void bc_reader_free(BCReaderState *s)
{
int i;
if (s->idx_to_atom) {
for(i = 0; i < s->idx_to_atom_count; i++) {
JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
}
js_free(s->ctx, s->idx_to_atom);
}
js_free(s->ctx, s->objects);
}
JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags)
{
BCReaderState ss, *s = &ss;
JSValue obj;
ctx->binary_object_count += 1;
ctx->binary_object_size += buf_len;
memset(s, 0, sizeof(*s));
s->ctx = ctx;
s->buf_start = buf;
s->buf_end = buf + buf_len;
s->ptr = buf;
s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
if (s->allow_bytecode)
s->first_atom = JS_ATOM_END;
else
s->first_atom = 1;
if (JS_ReadObjectAtoms(s)) {
obj = JS_EXCEPTION;
} else {
obj = JS_ReadObjectRec(s);
}
bc_reader_free(s);
return obj;
}
/*******************************************************************/
/* runtime functions & objects */
static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv);
static int check_function(JSContext *ctx, JSValueConst obj)
{
( run in 0.339 second using v1.01-cache-2.11-cpan-71847e10f99 )