mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
10fd8bdb70
This change resurrects ae5d06dc53
2240 lines
65 KiB
C
2240 lines
65 KiB
C
/*
|
|
* QuickJS Javascript Engine
|
|
*
|
|
* Copyright (c) 2017-2021 Fabrice Bellard
|
|
* Copyright (c) 2017-2021 Charlie Gordon
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include "libc/assert.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/str/str.h"
|
|
#include "third_party/quickjs/internal.h"
|
|
#include "third_party/quickjs/leb128.h"
|
|
#include "third_party/quickjs/libregexp.h"
|
|
#include "third_party/quickjs/quickjs.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
QuickJS (MIT License)\\n\
|
|
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
|
Copyright (c) 2017-2021 Charlie Gordon\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
/* clang-format off */
|
|
|
|
BOOL js_class_has_bytecode(JSClassID class_id)
|
|
{
|
|
return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
|
|
class_id == JS_CLASS_GENERATOR_FUNCTION ||
|
|
class_id == JS_CLASS_ASYNC_FUNCTION ||
|
|
class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
|
|
}
|
|
|
|
/* return NULL without exception if not a function or no bytecode */
|
|
JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
|
|
{
|
|
JSObject *p;
|
|
if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
|
|
return NULL;
|
|
p = JS_VALUE_GET_OBJ(val);
|
|
if (!js_class_has_bytecode(p->class_id))
|
|
return NULL;
|
|
return p->u.func.function_bytecode;
|
|
}
|
|
|
|
void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
|
|
{
|
|
int i;
|
|
#if 0
|
|
{
|
|
char buf[ATOM_GET_STR_BUF_SIZE];
|
|
printf("freeing %s\n",
|
|
JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
|
|
}
|
|
#endif
|
|
free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
|
|
if (b->vardefs) {
|
|
for(i = 0; i < b->arg_count + b->var_count; i++) {
|
|
JS_FreeAtomRT(rt, b->vardefs[i].var_name);
|
|
}
|
|
}
|
|
for(i = 0; i < b->cpool_count; i++)
|
|
JS_FreeValueRT(rt, b->cpool[i]);
|
|
for(i = 0; i < b->closure_var_count; i++) {
|
|
JSClosureVar *cv = &b->closure_var[i];
|
|
JS_FreeAtomRT(rt, cv->var_name);
|
|
}
|
|
if (b->realm)
|
|
JS_FreeContext(b->realm);
|
|
JS_FreeAtomRT(rt, b->func_name);
|
|
if (b->has_debug) {
|
|
JS_FreeAtomRT(rt, b->debug.filename);
|
|
js_free_rt(rt, b->debug.pc2line_buf);
|
|
js_free_rt(rt, b->debug.source);
|
|
}
|
|
remove_gc_object(&b->header);
|
|
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
|
|
list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
|
|
} else {
|
|
js_free_rt(rt, b);
|
|
}
|
|
}
|
|
|
|
void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
|
|
{
|
|
JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
|
|
JSFunctionBytecode *b;
|
|
JSVarRef **var_refs;
|
|
int i;
|
|
p1 = p->u.func.home_object;
|
|
if (p1) {
|
|
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
|
|
}
|
|
b = p->u.func.function_bytecode;
|
|
if (b) {
|
|
var_refs = p->u.func.var_refs;
|
|
if (var_refs) {
|
|
for(i = 0; i < b->closure_var_count; i++)
|
|
free_var_ref(rt, var_refs[i]);
|
|
js_free_rt(rt, var_refs);
|
|
}
|
|
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
|
|
}
|
|
}
|
|
|
|
void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
|
JSVarRef **var_refs = p->u.func.var_refs;
|
|
JSFunctionBytecode *b = p->u.func.function_bytecode;
|
|
int i;
|
|
if (p->u.func.home_object) {
|
|
JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
|
|
mark_func);
|
|
}
|
|
if (b) {
|
|
if (var_refs) {
|
|
for(i = 0; i < b->closure_var_count; i++) {
|
|
JSVarRef *var_ref = var_refs[i];
|
|
if (var_ref && var_ref->is_detached) {
|
|
mark_func(rt, &var_ref->header);
|
|
}
|
|
}
|
|
}
|
|
/* must mark the function bytecode because template objects may be
|
|
part of a cycle */
|
|
JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
|
|
}
|
|
}
|
|
|
|
int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
|
|
int byte_code_offset, uint32_t bc_len)
|
|
{
|
|
uint8_t *bc_buf;
|
|
int pos, len, op;
|
|
JSAtom atom;
|
|
uint32_t idx;
|
|
if (s->is_rom_data) {
|
|
/* directly use the input buffer */
|
|
if (UNLIKELY(s->buf_end - s->ptr < bc_len))
|
|
return bc_read_error_end(s);
|
|
bc_buf = (uint8_t *)s->ptr;
|
|
s->ptr += bc_len;
|
|
} else {
|
|
bc_buf = (void *)((uint8_t*)b + byte_code_offset);
|
|
if (bc_get_buf(s, bc_buf, bc_len))
|
|
return -1;
|
|
}
|
|
b->byte_code_buf = bc_buf;
|
|
pos = 0;
|
|
while (pos < bc_len) {
|
|
op = bc_buf[pos];
|
|
len = short_opcode_info(op).size;
|
|
switch(short_opcode_info(op).fmt) {
|
|
case OP_FMT_atom:
|
|
case OP_FMT_atom_u8:
|
|
case OP_FMT_atom_u16:
|
|
case OP_FMT_atom_label_u8:
|
|
case OP_FMT_atom_label_u16:
|
|
idx = get_u32(bc_buf + pos + 1);
|
|
if (s->is_rom_data) {
|
|
/* just increment the reference count of the atom */
|
|
JS_DupAtom(s->ctx, (JSAtom)idx);
|
|
} else {
|
|
if (bc_idx_to_atom(s, &atom, idx)) {
|
|
/* Note: the atoms will be freed up to this position */
|
|
b->byte_code_len = pos;
|
|
return -1;
|
|
}
|
|
put_u32(bc_buf + pos + 1, atom);
|
|
#ifdef DUMP_READ_OBJECT
|
|
bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pos += len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
typedef enum BCTagEnum {
|
|
BC_TAG_NULL = 1,
|
|
BC_TAG_UNDEFINED,
|
|
BC_TAG_BOOL_FALSE,
|
|
BC_TAG_BOOL_TRUE,
|
|
BC_TAG_INT32,
|
|
BC_TAG_FLOAT64,
|
|
BC_TAG_STRING,
|
|
BC_TAG_OBJECT,
|
|
BC_TAG_ARRAY,
|
|
BC_TAG_BIG_INT,
|
|
BC_TAG_BIG_FLOAT,
|
|
BC_TAG_BIG_DECIMAL,
|
|
BC_TAG_TEMPLATE_OBJECT,
|
|
BC_TAG_FUNCTION_BYTECODE,
|
|
BC_TAG_MODULE,
|
|
BC_TAG_TYPED_ARRAY,
|
|
BC_TAG_ARRAY_BUFFER,
|
|
BC_TAG_SHARED_ARRAY_BUFFER,
|
|
BC_TAG_DATE,
|
|
BC_TAG_OBJECT_VALUE,
|
|
BC_TAG_OBJECT_REFERENCE,
|
|
} BCTagEnum;
|
|
|
|
#ifdef CONFIG_BIGNUM
|
|
#define BC_BASE_VERSION 2
|
|
#else
|
|
#define BC_BASE_VERSION 1
|
|
#endif
|
|
#define BC_BE_VERSION 0x40
|
|
#ifdef WORDS_BIGENDIAN
|
|
#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)
|
|
#else
|
|
#define BC_VERSION BC_BASE_VERSION
|
|
#endif
|
|
|
|
typedef struct {
|
|
JSObject *obj;
|
|
uint32_t hash_next; /* -1 if no next entry */
|
|
} JSObjectListEntry;
|
|
|
|
/* XXX: reuse it to optimize weak references */
|
|
typedef struct {
|
|
JSObjectListEntry *object_tab;
|
|
int object_count;
|
|
int object_size;
|
|
uint32_t *hash_table;
|
|
uint32_t hash_size;
|
|
} JSObjectList;
|
|
|
|
static void js_object_list_init(JSObjectList *s)
|
|
{
|
|
bzero(s, sizeof(*s));
|
|
}
|
|
|
|
static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
|
|
{
|
|
return ((uintptr_t)p * 3163) & (hash_size - 1);
|
|
}
|
|
|
|
static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
|
|
uint32_t new_hash_size)
|
|
{
|
|
JSObjectListEntry *e;
|
|
uint32_t i, h, *new_hash_table;
|
|
new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
|
|
if (!new_hash_table)
|
|
return -1;
|
|
js_free(ctx, s->hash_table);
|
|
s->hash_table = new_hash_table;
|
|
s->hash_size = new_hash_size;
|
|
for(i = 0; i < s->hash_size; i++) {
|
|
s->hash_table[i] = -1;
|
|
}
|
|
for(i = 0; i < s->object_count; i++) {
|
|
e = &s->object_tab[i];
|
|
h = js_object_list_get_hash(e->obj, s->hash_size);
|
|
e->hash_next = s->hash_table[h];
|
|
s->hash_table[h] = i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
|
|
memory error */
|
|
static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
|
|
{
|
|
JSObjectListEntry *e;
|
|
uint32_t h, new_hash_size;
|
|
if (js_resize_array(ctx, (void *)&s->object_tab,
|
|
sizeof(s->object_tab[0]),
|
|
&s->object_size, s->object_count + 1))
|
|
return -1;
|
|
if (UNLIKELY((s->object_count + 1) >= s->hash_size)) {
|
|
new_hash_size = max_uint32(s->hash_size, 4);
|
|
while (new_hash_size <= s->object_count)
|
|
new_hash_size *= 2;
|
|
if (js_object_list_resize_hash(ctx, s, new_hash_size))
|
|
return -1;
|
|
}
|
|
e = &s->object_tab[s->object_count++];
|
|
h = js_object_list_get_hash(obj, s->hash_size);
|
|
e->obj = obj;
|
|
e->hash_next = s->hash_table[h];
|
|
s->hash_table[h] = s->object_count - 1;
|
|
return 0;
|
|
}
|
|
|
|
/* return -1 if not present or the object index */
|
|
static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
|
|
{
|
|
JSObjectListEntry *e;
|
|
uint32_t h, p;
|
|
/* must test empty size because there is no hash table */
|
|
if (s->object_count == 0)
|
|
return -1;
|
|
h = js_object_list_get_hash(obj, s->hash_size);
|
|
p = s->hash_table[h];
|
|
while (p != -1) {
|
|
e = &s->object_tab[p];
|
|
if (e->obj == obj)
|
|
return p;
|
|
p = e->hash_next;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void js_object_list_end(JSContext *ctx, JSObjectList *s)
|
|
{
|
|
js_free(ctx, s->object_tab);
|
|
js_free(ctx, s->hash_table);
|
|
}
|
|
|
|
typedef struct BCWriterState {
|
|
JSContext *ctx;
|
|
DynBuf dbuf;
|
|
BOOL byte_swap : 8;
|
|
BOOL allow_bytecode : 8;
|
|
BOOL allow_sab : 8;
|
|
BOOL allow_reference : 8;
|
|
uint32_t first_atom;
|
|
uint32_t *atom_to_idx;
|
|
int atom_to_idx_size;
|
|
JSAtom *idx_to_atom;
|
|
int idx_to_atom_count;
|
|
int idx_to_atom_size;
|
|
uint8_t **sab_tab;
|
|
int sab_tab_len;
|
|
int sab_tab_size;
|
|
/* list of referenced objects (used if allow_reference = TRUE) */
|
|
JSObjectList object_list;
|
|
} BCWriterState;
|
|
|
|
#ifdef DUMP_READ_OBJECT
|
|
static const char * const bc_tag_str[] = {
|
|
"invalid",
|
|
"null",
|
|
"undefined",
|
|
"false",
|
|
"true",
|
|
"int32",
|
|
"float64",
|
|
"string",
|
|
"object",
|
|
"array",
|
|
"bigint",
|
|
"bigfloat",
|
|
"bigdecimal",
|
|
"template",
|
|
"function",
|
|
"module",
|
|
"TypedArray",
|
|
"ArrayBuffer",
|
|
"SharedArrayBuffer",
|
|
"Date",
|
|
"ObjectValue",
|
|
"ObjectReference",
|
|
};
|
|
#endif
|
|
|
|
static void bc_put_u8(BCWriterState *s, uint8_t v)
|
|
{
|
|
dbuf_putc(&s->dbuf, v);
|
|
}
|
|
|
|
static void bc_put_u16(BCWriterState *s, uint16_t v)
|
|
{
|
|
if (s->byte_swap)
|
|
v = bswap16(v);
|
|
dbuf_put_u16(&s->dbuf, v);
|
|
}
|
|
|
|
static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
|
|
{
|
|
if (s->byte_swap)
|
|
v = bswap32(v);
|
|
dbuf_put_u32(&s->dbuf, v);
|
|
}
|
|
|
|
static void bc_put_u64(BCWriterState *s, uint64_t v)
|
|
{
|
|
if (s->byte_swap)
|
|
v = bswap64(v);
|
|
dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
|
|
}
|
|
|
|
static void bc_put_leb128(BCWriterState *s, uint32_t v)
|
|
{
|
|
dbuf_put_leb128(&s->dbuf, v);
|
|
}
|
|
|
|
static void bc_put_sleb128(BCWriterState *s, int32_t v)
|
|
{
|
|
dbuf_put_sleb128(&s->dbuf, v);
|
|
}
|
|
|
|
static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
|
|
{
|
|
*pflags = *pflags | (val << *pidx);
|
|
*pidx += n;
|
|
}
|
|
|
|
static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
|
|
{
|
|
uint32_t v;
|
|
if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
|
|
*pres = atom;
|
|
return 0;
|
|
}
|
|
atom -= s->first_atom;
|
|
if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
|
|
*pres = s->atom_to_idx[atom];
|
|
return 0;
|
|
}
|
|
if (atom >= s->atom_to_idx_size) {
|
|
int old_size, i;
|
|
old_size = s->atom_to_idx_size;
|
|
if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
|
|
sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
|
|
atom + 1))
|
|
return -1;
|
|
/* XXX: could add a specific js_resize_array() function to do it */
|
|
for(i = old_size; i < s->atom_to_idx_size; i++)
|
|
s->atom_to_idx[i] = 0;
|
|
}
|
|
if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
|
|
sizeof(s->idx_to_atom[0]),
|
|
&s->idx_to_atom_size, s->idx_to_atom_count + 1))
|
|
goto fail;
|
|
v = s->idx_to_atom_count++;
|
|
s->idx_to_atom[v] = atom + s->first_atom;
|
|
v += s->first_atom;
|
|
s->atom_to_idx[atom] = v;
|
|
*pres = v;
|
|
return 0;
|
|
fail:
|
|
*pres = 0;
|
|
return -1;
|
|
}
|
|
|
|
static int bc_put_atom(BCWriterState *s, JSAtom atom)
|
|
{
|
|
uint32_t v;
|
|
if (__JS_AtomIsTaggedInt(atom)) {
|
|
v = (__JS_AtomToUInt32(atom) << 1) | 1;
|
|
} else {
|
|
if (bc_atom_to_idx(s, &v, atom))
|
|
return -1;
|
|
v <<= 1;
|
|
}
|
|
bc_put_leb128(s, v);
|
|
return 0;
|
|
}
|
|
|
|
static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
|
|
{
|
|
int pos, len, op, fmt;
|
|
pos = 0;
|
|
while (pos < bc_len) {
|
|
op = bc_buf[pos];
|
|
len = short_opcode_info(op).size;
|
|
fmt = short_opcode_info(op).fmt;
|
|
switch(fmt) {
|
|
case OP_FMT_u16:
|
|
case OP_FMT_i16:
|
|
case OP_FMT_label16:
|
|
case OP_FMT_npop:
|
|
case OP_FMT_loc:
|
|
case OP_FMT_arg:
|
|
case OP_FMT_var_ref:
|
|
put_u16(bc_buf + pos + 1,
|
|
bswap16(get_u16(bc_buf + pos + 1)));
|
|
break;
|
|
case OP_FMT_i32:
|
|
case OP_FMT_u32:
|
|
case OP_FMT_const:
|
|
case OP_FMT_label:
|
|
case OP_FMT_atom:
|
|
case OP_FMT_atom_u8:
|
|
put_u32(bc_buf + pos + 1,
|
|
bswap32(get_u32(bc_buf + pos + 1)));
|
|
break;
|
|
case OP_FMT_atom_u16:
|
|
case OP_FMT_label_u16:
|
|
put_u32(bc_buf + pos + 1,
|
|
bswap32(get_u32(bc_buf + pos + 1)));
|
|
put_u16(bc_buf + pos + 1 + 4,
|
|
bswap16(get_u16(bc_buf + pos + 1 + 4)));
|
|
break;
|
|
case OP_FMT_atom_label_u8:
|
|
case OP_FMT_atom_label_u16:
|
|
put_u32(bc_buf + pos + 1,
|
|
bswap32(get_u32(bc_buf + pos + 1)));
|
|
put_u32(bc_buf + pos + 1 + 4,
|
|
bswap32(get_u32(bc_buf + pos + 1 + 4)));
|
|
if (fmt == OP_FMT_atom_label_u16) {
|
|
put_u16(bc_buf + pos + 1 + 4 + 4,
|
|
bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
|
|
}
|
|
break;
|
|
case OP_FMT_npop_u16:
|
|
put_u16(bc_buf + pos + 1,
|
|
bswap16(get_u16(bc_buf + pos + 1)));
|
|
put_u16(bc_buf + pos + 1 + 2,
|
|
bswap16(get_u16(bc_buf + pos + 1 + 2)));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pos += len;
|
|
}
|
|
}
|
|
|
|
static int JS_WriteFunctionBytecode(BCWriterState *s,
|
|
const uint8_t *bc_buf1, int bc_len)
|
|
{
|
|
int pos, len, op;
|
|
JSAtom atom;
|
|
uint8_t *bc_buf;
|
|
uint32_t val;
|
|
bc_buf = js_malloc(s->ctx, bc_len);
|
|
if (!bc_buf)
|
|
return -1;
|
|
memcpy(bc_buf, bc_buf1, bc_len);
|
|
pos = 0;
|
|
while (pos < bc_len) {
|
|
op = bc_buf[pos];
|
|
len = short_opcode_info(op).size;
|
|
switch(short_opcode_info(op).fmt) {
|
|
case OP_FMT_atom:
|
|
case OP_FMT_atom_u8:
|
|
case OP_FMT_atom_u16:
|
|
case OP_FMT_atom_label_u8:
|
|
case OP_FMT_atom_label_u16:
|
|
atom = get_u32(bc_buf + pos + 1);
|
|
if (bc_atom_to_idx(s, &val, atom))
|
|
goto fail;
|
|
put_u32(bc_buf + pos + 1, val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pos += len;
|
|
}
|
|
if (s->byte_swap)
|
|
bc_byte_swap(bc_buf, bc_len);
|
|
dbuf_put(&s->dbuf, bc_buf, bc_len);
|
|
js_free(s->ctx, bc_buf);
|
|
return 0;
|
|
fail:
|
|
js_free(s->ctx, bc_buf);
|
|
return -1;
|
|
}
|
|
|
|
static void JS_WriteString(BCWriterState *s, JSString *p)
|
|
{
|
|
int i;
|
|
bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
|
|
if (p->is_wide_char) {
|
|
for(i = 0; i < p->len; i++)
|
|
bc_put_u16(s, p->u.str16[i]);
|
|
} else {
|
|
dbuf_put(&s->dbuf, p->u.str8, p->len);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_BIGNUM
|
|
static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
uint32_t tag, tag1;
|
|
int64_t e;
|
|
JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
|
|
bf_t *a = &bf->num;
|
|
size_t len, i, n1, j;
|
|
limb_t v;
|
|
tag = JS_VALUE_GET_TAG(obj);
|
|
switch(tag) {
|
|
case JS_TAG_BIG_INT:
|
|
tag1 = BC_TAG_BIG_INT;
|
|
break;
|
|
case JS_TAG_BIG_FLOAT:
|
|
tag1 = BC_TAG_BIG_FLOAT;
|
|
break;
|
|
case JS_TAG_BIG_DECIMAL:
|
|
tag1 = BC_TAG_BIG_DECIMAL;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
bc_put_u8(s, tag1);
|
|
/* sign + exponent */
|
|
if (a->expn == BF_EXP_ZERO)
|
|
e = 0;
|
|
else if (a->expn == BF_EXP_INF)
|
|
e = 1;
|
|
else if (a->expn == BF_EXP_NAN)
|
|
e = 2;
|
|
else if (a->expn >= 0)
|
|
e = a->expn + 3;
|
|
else
|
|
e = a->expn;
|
|
e = (e << 1) | a->sign;
|
|
if (e < INT32_MIN || e > INT32_MAX) {
|
|
JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
|
|
return -1;
|
|
}
|
|
bc_put_sleb128(s, e);
|
|
/* mantissa */
|
|
if (a->len != 0) {
|
|
if (tag != JS_TAG_BIG_DECIMAL) {
|
|
i = 0;
|
|
while (i < a->len && a->tab[i] == 0)
|
|
i++;
|
|
assert(i < a->len);
|
|
v = a->tab[i];
|
|
n1 = sizeof(limb_t);
|
|
while ((v & 0xff) == 0) {
|
|
n1--;
|
|
v >>= 8;
|
|
}
|
|
i++;
|
|
len = (a->len - i) * sizeof(limb_t) + n1;
|
|
if (len > INT32_MAX) {
|
|
JS_ThrowInternalError(s->ctx, "bignum is too large");
|
|
return -1;
|
|
}
|
|
bc_put_leb128(s, len);
|
|
/* always saved in byte based little endian representation */
|
|
for(j = 0; j < n1; j++) {
|
|
dbuf_putc(&s->dbuf, v >> (j * 8));
|
|
}
|
|
for(; i < a->len; i++) {
|
|
limb_t v = a->tab[i];
|
|
#if LIMB_BITS == 32
|
|
#ifdef WORDS_BIGENDIAN
|
|
v = bswap32(v);
|
|
#endif
|
|
dbuf_put_u32(&s->dbuf, v);
|
|
#else
|
|
#ifdef WORDS_BIGENDIAN
|
|
v = bswap64(v);
|
|
#endif
|
|
dbuf_put_u64(&s->dbuf, v);
|
|
#endif
|
|
}
|
|
} else {
|
|
int bpos, d;
|
|
uint8_t v8;
|
|
size_t i0;
|
|
/* little endian BCD */
|
|
i = 0;
|
|
while (i < a->len && a->tab[i] == 0)
|
|
i++;
|
|
assert(i < a->len);
|
|
len = a->len * LIMB_DIGITS;
|
|
v = a->tab[i];
|
|
j = 0;
|
|
while ((v % 10) == 0) {
|
|
j++;
|
|
v /= 10;
|
|
}
|
|
len -= j;
|
|
assert(len > 0);
|
|
if (len > INT32_MAX) {
|
|
JS_ThrowInternalError(s->ctx, "bignum is too large");
|
|
return -1;
|
|
}
|
|
bc_put_leb128(s, len);
|
|
bpos = 0;
|
|
v8 = 0;
|
|
i0 = i;
|
|
for(; i < a->len; i++) {
|
|
if (i != i0) {
|
|
v = a->tab[i];
|
|
j = 0;
|
|
}
|
|
for(; j < LIMB_DIGITS; j++) {
|
|
d = v % 10;
|
|
v /= 10;
|
|
if (bpos == 0) {
|
|
v8 = d;
|
|
bpos = 1;
|
|
} else {
|
|
dbuf_putc(&s->dbuf, v8 | (d << 4));
|
|
bpos = 0;
|
|
}
|
|
}
|
|
}
|
|
/* flush the last digit */
|
|
if (bpos) {
|
|
dbuf_putc(&s->dbuf, v8);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BIGNUM */
|
|
|
|
static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
|
|
|
|
static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
|
|
uint32_t flags;
|
|
int idx, i;
|
|
bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
|
|
flags = idx = 0;
|
|
bc_set_flags(&flags, &idx, b->has_prototype, 1);
|
|
bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
|
|
bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
|
|
bc_set_flags(&flags, &idx, b->need_home_object, 1);
|
|
bc_set_flags(&flags, &idx, b->func_kind, 2);
|
|
bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
|
|
bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
|
|
bc_set_flags(&flags, &idx, b->super_allowed, 1);
|
|
bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
|
|
bc_set_flags(&flags, &idx, b->has_debug, 1);
|
|
bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
|
|
assert(idx <= 16);
|
|
bc_put_u16(s, flags);
|
|
bc_put_u8(s, b->js_mode);
|
|
bc_put_atom(s, b->func_name);
|
|
bc_put_leb128(s, b->arg_count);
|
|
bc_put_leb128(s, b->var_count);
|
|
bc_put_leb128(s, b->defined_arg_count);
|
|
bc_put_leb128(s, b->stack_size);
|
|
bc_put_leb128(s, b->closure_var_count);
|
|
bc_put_leb128(s, b->cpool_count);
|
|
bc_put_leb128(s, b->byte_code_len);
|
|
if (b->vardefs) {
|
|
/* XXX: this field is redundant */
|
|
bc_put_leb128(s, b->arg_count + b->var_count);
|
|
for(i = 0; i < b->arg_count + b->var_count; i++) {
|
|
JSVarDef *vd = &b->vardefs[i];
|
|
bc_put_atom(s, vd->var_name);
|
|
bc_put_leb128(s, vd->scope_level);
|
|
bc_put_leb128(s, vd->scope_next + 1);
|
|
flags = idx = 0;
|
|
bc_set_flags(&flags, &idx, vd->var_kind, 4);
|
|
bc_set_flags(&flags, &idx, vd->is_const, 1);
|
|
bc_set_flags(&flags, &idx, vd->is_lexical, 1);
|
|
bc_set_flags(&flags, &idx, vd->is_captured, 1);
|
|
assert(idx <= 8);
|
|
bc_put_u8(s, flags);
|
|
}
|
|
} else {
|
|
bc_put_leb128(s, 0);
|
|
}
|
|
for(i = 0; i < b->closure_var_count; i++) {
|
|
JSClosureVar *cv = &b->closure_var[i];
|
|
bc_put_atom(s, cv->var_name);
|
|
bc_put_leb128(s, cv->var_idx);
|
|
flags = idx = 0;
|
|
bc_set_flags(&flags, &idx, cv->is_local, 1);
|
|
bc_set_flags(&flags, &idx, cv->is_arg, 1);
|
|
bc_set_flags(&flags, &idx, cv->is_const, 1);
|
|
bc_set_flags(&flags, &idx, cv->is_lexical, 1);
|
|
bc_set_flags(&flags, &idx, cv->var_kind, 4);
|
|
assert(idx <= 8);
|
|
bc_put_u8(s, flags);
|
|
}
|
|
if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
|
|
goto fail;
|
|
if (b->has_debug) {
|
|
bc_put_atom(s, b->debug.filename);
|
|
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);
|
|
}
|
|
for(i = 0; i < b->cpool_count; i++) {
|
|
if (JS_WriteObjectRec(s, b->cpool[i]))
|
|
goto fail;
|
|
}
|
|
return 0;
|
|
fail:
|
|
return -1;
|
|
}
|
|
|
|
static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
JSModuleDef *m = JS_VALUE_GET_PTR(obj);
|
|
int i;
|
|
bc_put_u8(s, BC_TAG_MODULE);
|
|
bc_put_atom(s, m->module_name);
|
|
bc_put_leb128(s, m->req_module_entries_count);
|
|
for(i = 0; i < m->req_module_entries_count; i++) {
|
|
JSReqModuleEntry *rme = &m->req_module_entries[i];
|
|
bc_put_atom(s, rme->module_name);
|
|
}
|
|
bc_put_leb128(s, m->export_entries_count);
|
|
for(i = 0; i < m->export_entries_count; i++) {
|
|
JSExportEntry *me = &m->export_entries[i];
|
|
bc_put_u8(s, me->export_type);
|
|
if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
|
|
bc_put_leb128(s, me->u.local.var_idx);
|
|
} else {
|
|
bc_put_leb128(s, me->u.req_module_idx);
|
|
bc_put_atom(s, me->local_name);
|
|
}
|
|
bc_put_atom(s, me->export_name);
|
|
}
|
|
bc_put_leb128(s, m->star_export_entries_count);
|
|
for(i = 0; i < m->star_export_entries_count; i++) {
|
|
JSStarExportEntry *se = &m->star_export_entries[i];
|
|
bc_put_leb128(s, se->req_module_idx);
|
|
}
|
|
bc_put_leb128(s, m->import_entries_count);
|
|
for(i = 0; i < m->import_entries_count; i++) {
|
|
JSImportEntry *mi = &m->import_entries[i];
|
|
bc_put_leb128(s, mi->var_idx);
|
|
bc_put_atom(s, mi->import_name);
|
|
bc_put_leb128(s, mi->req_module_idx);
|
|
}
|
|
if (JS_WriteObjectRec(s, m->func_obj))
|
|
goto fail;
|
|
return 0;
|
|
fail:
|
|
return -1;
|
|
}
|
|
|
|
static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
|
uint32_t i, len;
|
|
JSValue val;
|
|
int ret;
|
|
BOOL is_template;
|
|
if (s->allow_bytecode && !p->extensible) {
|
|
/* not extensible array: we consider it is a
|
|
template when we are saving bytecode */
|
|
bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
|
|
is_template = TRUE;
|
|
} else {
|
|
bc_put_u8(s, BC_TAG_ARRAY);
|
|
is_template = FALSE;
|
|
}
|
|
if (js_get_length32(s->ctx, &len, obj))
|
|
goto fail1;
|
|
bc_put_leb128(s, len);
|
|
for(i = 0; i < len; i++) {
|
|
val = JS_GetPropertyUint32(s->ctx, obj, i);
|
|
if (JS_IsException(val))
|
|
goto fail1;
|
|
ret = JS_WriteObjectRec(s, val);
|
|
JS_FreeValue(s->ctx, val);
|
|
if (ret)
|
|
goto fail1;
|
|
}
|
|
if (is_template) {
|
|
val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
|
|
if (JS_IsException(val))
|
|
goto fail1;
|
|
ret = JS_WriteObjectRec(s, val);
|
|
JS_FreeValue(s->ctx, val);
|
|
if (ret)
|
|
goto fail1;
|
|
}
|
|
return 0;
|
|
fail1:
|
|
return -1;
|
|
}
|
|
|
|
static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
|
|
{
|
|
return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
|
|
}
|
|
|
|
static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
|
uint32_t i, prop_count;
|
|
JSShape *sh;
|
|
JSShapeProperty *pr;
|
|
int pass;
|
|
JSAtom atom;
|
|
bc_put_u8(s, BC_TAG_OBJECT);
|
|
prop_count = 0;
|
|
sh = p->shape;
|
|
for(pass = 0; pass < 2; pass++) {
|
|
if (pass == 1)
|
|
bc_put_leb128(s, prop_count);
|
|
for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
|
|
atom = pr->atom;
|
|
if (atom != JS_ATOM_NULL &&
|
|
JS_AtomIsString(s->ctx, atom) &&
|
|
(pr->flags & JS_PROP_ENUMERABLE)) {
|
|
if (pr->flags & JS_PROP_TMASK) {
|
|
JS_ThrowTypeError(s->ctx, "only value properties are supported");
|
|
goto fail;
|
|
}
|
|
if (pass == 0) {
|
|
prop_count++;
|
|
} else {
|
|
bc_put_atom(s, atom);
|
|
if (JS_WriteObjectRec(s, p->prop[i].u.value))
|
|
goto fail;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
fail:
|
|
return -1;
|
|
}
|
|
|
|
static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
|
JSTypedArray *ta = p->u.typed_array;
|
|
bc_put_u8(s, BC_TAG_TYPED_ARRAY);
|
|
bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
|
|
bc_put_leb128(s, p->u.array.count);
|
|
bc_put_leb128(s, ta->offset);
|
|
if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
|
JSArrayBuffer *abuf = p->u.array_buffer;
|
|
if (abuf->detached) {
|
|
JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
|
|
return -1;
|
|
}
|
|
bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
|
|
bc_put_leb128(s, abuf->byte_length);
|
|
dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
|
|
return 0;
|
|
}
|
|
|
|
static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
|
JSArrayBuffer *abuf = p->u.array_buffer;
|
|
assert(!abuf->detached); /* SharedArrayBuffer are never detached */
|
|
bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
|
|
bc_put_leb128(s, abuf->byte_length);
|
|
bc_put_u64(s, (uintptr_t)abuf->data);
|
|
if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
|
|
&s->sab_tab_size, s->sab_tab_len + 1))
|
|
return -1;
|
|
/* keep the SAB pointer so that the user can clone it or free it */
|
|
s->sab_tab[s->sab_tab_len++] = abuf->data;
|
|
return 0;
|
|
}
|
|
|
|
static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
|
|
{
|
|
uint32_t tag;
|
|
if (js_check_stack_overflow(s->ctx->rt, 0)) {
|
|
JS_ThrowStackOverflow(s->ctx);
|
|
return -1;
|
|
}
|
|
tag = JS_VALUE_GET_NORM_TAG(obj);
|
|
switch(tag) {
|
|
case JS_TAG_NULL:
|
|
bc_put_u8(s, BC_TAG_NULL);
|
|
break;
|
|
case JS_TAG_UNDEFINED:
|
|
bc_put_u8(s, BC_TAG_UNDEFINED);
|
|
break;
|
|
case JS_TAG_BOOL:
|
|
bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
|
|
break;
|
|
case JS_TAG_INT:
|
|
bc_put_u8(s, BC_TAG_INT32);
|
|
bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
|
|
break;
|
|
case JS_TAG_FLOAT64:
|
|
{
|
|
JSFloat64Union u;
|
|
bc_put_u8(s, BC_TAG_FLOAT64);
|
|
u.d = JS_VALUE_GET_FLOAT64(obj);
|
|
bc_put_u64(s, u.u64);
|
|
}
|
|
break;
|
|
case JS_TAG_STRING:
|
|
{
|
|
JSString *p = JS_VALUE_GET_STRING(obj);
|
|
bc_put_u8(s, BC_TAG_STRING);
|
|
JS_WriteString(s, p);
|
|
}
|
|
break;
|
|
case JS_TAG_FUNCTION_BYTECODE:
|
|
if (!s->allow_bytecode)
|
|
goto invalid_tag;
|
|
if (JS_WriteFunctionTag(s, obj))
|
|
goto fail;
|
|
break;
|
|
case JS_TAG_MODULE:
|
|
if (!s->allow_bytecode)
|
|
goto invalid_tag;
|
|
if (JS_WriteModule(s, obj))
|
|
goto fail;
|
|
break;
|
|
case JS_TAG_OBJECT:
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
|
int ret, idx;
|
|
if (s->allow_reference) {
|
|
idx = js_object_list_find(s->ctx, &s->object_list, p);
|
|
if (idx >= 0) {
|
|
bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
|
|
bc_put_leb128(s, idx);
|
|
break;
|
|
} else {
|
|
if (js_object_list_add(s->ctx, &s->object_list, p))
|
|
goto fail;
|
|
}
|
|
} else {
|
|
if (p->tmp_mark) {
|
|
JS_ThrowTypeError(s->ctx, "circular reference");
|
|
goto fail;
|
|
}
|
|
p->tmp_mark = 1;
|
|
}
|
|
switch(p->class_id) {
|
|
case JS_CLASS_ARRAY:
|
|
ret = JS_WriteArray(s, obj);
|
|
break;
|
|
case JS_CLASS_OBJECT:
|
|
ret = JS_WriteObjectTag(s, obj);
|
|
break;
|
|
case JS_CLASS_ARRAY_BUFFER:
|
|
ret = JS_WriteArrayBuffer(s, obj);
|
|
break;
|
|
case JS_CLASS_SHARED_ARRAY_BUFFER:
|
|
if (!s->allow_sab)
|
|
goto invalid_tag;
|
|
ret = JS_WriteSharedArrayBuffer(s, obj);
|
|
break;
|
|
case JS_CLASS_DATE:
|
|
bc_put_u8(s, BC_TAG_DATE);
|
|
ret = JS_WriteObjectRec(s, p->u.object_data);
|
|
break;
|
|
case JS_CLASS_NUMBER:
|
|
case JS_CLASS_STRING:
|
|
case JS_CLASS_BOOLEAN:
|
|
#ifdef CONFIG_BIGNUM
|
|
case JS_CLASS_BIG_INT:
|
|
case JS_CLASS_BIG_FLOAT:
|
|
case JS_CLASS_BIG_DECIMAL:
|
|
#endif
|
|
bc_put_u8(s, BC_TAG_OBJECT_VALUE);
|
|
ret = JS_WriteObjectRec(s, p->u.object_data);
|
|
break;
|
|
default:
|
|
if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
|
|
p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
|
|
ret = JS_WriteTypedArray(s, obj);
|
|
} else {
|
|
JS_ThrowTypeError(s->ctx, "unsupported object class");
|
|
ret = -1;
|
|
}
|
|
break;
|
|
}
|
|
p->tmp_mark = 0;
|
|
if (ret)
|
|
goto fail;
|
|
}
|
|
break;
|
|
#ifdef CONFIG_BIGNUM
|
|
case JS_TAG_BIG_INT:
|
|
case JS_TAG_BIG_FLOAT:
|
|
case JS_TAG_BIG_DECIMAL:
|
|
if (JS_WriteBigNum(s, obj))
|
|
goto fail;
|
|
break;
|
|
#endif
|
|
default:
|
|
invalid_tag:
|
|
JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
|
|
goto fail;
|
|
}
|
|
return 0;
|
|
fail:
|
|
return -1;
|
|
}
|
|
|
|
/* create the atom table */
|
|
static int JS_WriteObjectAtoms(BCWriterState *s)
|
|
{
|
|
JSRuntime *rt = s->ctx->rt;
|
|
DynBuf dbuf1;
|
|
int i, atoms_size;
|
|
uint8_t version;
|
|
dbuf1 = s->dbuf;
|
|
js_dbuf_init(s->ctx, &s->dbuf);
|
|
version = BC_VERSION;
|
|
if (s->byte_swap)
|
|
version ^= BC_BE_VERSION;
|
|
bc_put_u8(s, version);
|
|
bc_put_leb128(s, s->idx_to_atom_count);
|
|
for(i = 0; i < s->idx_to_atom_count; i++) {
|
|
JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
|
|
JS_WriteString(s, p);
|
|
}
|
|
/* XXX: should check for OOM in above phase */
|
|
/* move the atoms at the start */
|
|
/* XXX: could just append dbuf1 data, but it uses more memory if
|
|
dbuf1 is larger than dbuf */
|
|
atoms_size = s->dbuf.size;
|
|
if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))
|
|
goto fail;
|
|
memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
|
|
memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
|
|
dbuf1.size += atoms_size;
|
|
dbuf_free(&s->dbuf);
|
|
s->dbuf = dbuf1;
|
|
return 0;
|
|
fail:
|
|
dbuf_free(&dbuf1);
|
|
return -1;
|
|
}
|
|
|
|
uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
|
|
int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
|
|
{
|
|
BCWriterState ss, *s = &ss;
|
|
bzero(s, sizeof(*s));
|
|
s->ctx = ctx;
|
|
/* XXX: byte swapped output is untested */
|
|
s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);
|
|
s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
|
|
s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
|
|
s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
|
|
/* XXX: could use a different version when bytecode is included */
|
|
if (s->allow_bytecode)
|
|
s->first_atom = JS_ATOM_END;
|
|
else
|
|
s->first_atom = 1;
|
|
js_dbuf_init(ctx, &s->dbuf);
|
|
js_object_list_init(&s->object_list);
|
|
if (JS_WriteObjectRec(s, obj))
|
|
goto fail;
|
|
if (JS_WriteObjectAtoms(s))
|
|
goto fail;
|
|
js_object_list_end(ctx, &s->object_list);
|
|
js_free(ctx, s->atom_to_idx);
|
|
js_free(ctx, s->idx_to_atom);
|
|
*psize = s->dbuf.size;
|
|
if (psab_tab)
|
|
*psab_tab = s->sab_tab;
|
|
if (psab_tab_len)
|
|
*psab_tab_len = s->sab_tab_len;
|
|
return s->dbuf.buf;
|
|
fail:
|
|
js_object_list_end(ctx, &s->object_list);
|
|
js_free(ctx, s->atom_to_idx);
|
|
js_free(ctx, s->idx_to_atom);
|
|
dbuf_free(&s->dbuf);
|
|
*psize = 0;
|
|
if (psab_tab)
|
|
*psab_tab = NULL;
|
|
if (psab_tab_len)
|
|
*psab_tab_len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
|
|
int flags)
|
|
{
|
|
return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
|
|
}
|
|
|
|
#ifdef DUMP_READ_OBJECT
|
|
static void printfesque(2) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
|
|
va_list ap;
|
|
int i, n, n0;
|
|
if (!s->ptr_last)
|
|
s->ptr_last = s->buf_start;
|
|
n = n0 = 0;
|
|
if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
|
|
n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
|
|
n += n0;
|
|
}
|
|
for (i = 0; s->ptr_last < s->ptr; i++) {
|
|
if ((i & 7) == 0 && i > 0) {
|
|
printf("\n%*s", n0, "");
|
|
n = n0;
|
|
}
|
|
n += printf(" %02x", *s->ptr_last++);
|
|
}
|
|
if (*fmt == '}')
|
|
s->level--;
|
|
if (n < 32 + s->level * 2) {
|
|
printf("%*s", 32 + s->level * 2 - n, "");
|
|
}
|
|
va_start(ap, fmt);
|
|
vfprintf(stdout, fmt, ap);
|
|
va_end(ap);
|
|
if (strchr(fmt, '{'))
|
|
s->level++;
|
|
}
|
|
#else
|
|
#define bc_read_trace(...)
|
|
#endif
|
|
|
|
int bc_read_error_end(BCReaderState *s)
|
|
{
|
|
if (!s->error_state) {
|
|
JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
|
|
}
|
|
return s->error_state = -1;
|
|
}
|
|
|
|
static int bc_get_u8(BCReaderState *s, uint8_t *pval)
|
|
{
|
|
if (UNLIKELY(s->buf_end - s->ptr < 1)) {
|
|
*pval = 0; /* avoid warning */
|
|
return bc_read_error_end(s);
|
|
}
|
|
*pval = *s->ptr++;
|
|
return 0;
|
|
}
|
|
|
|
static int bc_get_u16(BCReaderState *s, uint16_t *pval)
|
|
{
|
|
if (UNLIKELY(s->buf_end - s->ptr < 2)) {
|
|
*pval = 0; /* avoid warning */
|
|
return bc_read_error_end(s);
|
|
}
|
|
*pval = get_u16(s->ptr);
|
|
s->ptr += 2;
|
|
return 0;
|
|
}
|
|
|
|
static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
|
|
{
|
|
if (UNLIKELY(s->buf_end - s->ptr < 4)) {
|
|
*pval = 0; /* avoid warning */
|
|
return bc_read_error_end(s);
|
|
}
|
|
*pval = get_u32(s->ptr);
|
|
s->ptr += 4;
|
|
return 0;
|
|
}
|
|
|
|
static int bc_get_u64(BCReaderState *s, uint64_t *pval)
|
|
{
|
|
if (UNLIKELY(s->buf_end - s->ptr < 8)) {
|
|
*pval = 0; /* avoid warning */
|
|
return bc_read_error_end(s);
|
|
}
|
|
*pval = get_u64(s->ptr);
|
|
s->ptr += 8;
|
|
return 0;
|
|
}
|
|
|
|
static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
|
|
{
|
|
int ret;
|
|
ret = get_leb128(pval, s->ptr, s->buf_end);
|
|
if (UNLIKELY(ret < 0))
|
|
return bc_read_error_end(s);
|
|
s->ptr += ret;
|
|
return 0;
|
|
}
|
|
|
|
static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
|
|
{
|
|
int ret;
|
|
ret = get_sleb128(pval, s->ptr, s->buf_end);
|
|
if (UNLIKELY(ret < 0))
|
|
return bc_read_error_end(s);
|
|
s->ptr += ret;
|
|
return 0;
|
|
}
|
|
|
|
/* XXX: used to read an `int` with a positive value */
|
|
static int bc_get_leb128_int(BCReaderState *s, int *pval)
|
|
{
|
|
return bc_get_leb128(s, (uint32_t *)pval);
|
|
}
|
|
|
|
static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
|
|
{
|
|
uint32_t val;
|
|
if (bc_get_leb128(s, &val)) {
|
|
*pval = 0;
|
|
return -1;
|
|
}
|
|
*pval = val;
|
|
return 0;
|
|
}
|
|
|
|
int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
|
|
{
|
|
if (buf_len != 0) {
|
|
if (UNLIKELY(!buf || s->buf_end - s->ptr < buf_len))
|
|
return bc_read_error_end(s);
|
|
memcpy(buf, s->ptr, buf_len);
|
|
s->ptr += buf_len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
|
|
{
|
|
JSAtom atom;
|
|
if (__JS_AtomIsTaggedInt(idx)) {
|
|
atom = idx;
|
|
} else if (idx < s->first_atom) {
|
|
atom = JS_DupAtom(s->ctx, idx);
|
|
} else {
|
|
idx -= s->first_atom;
|
|
if (idx >= s->idx_to_atom_count) {
|
|
JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
|
|
(unsigned int)(s->ptr - s->buf_start));
|
|
*patom = JS_ATOM_NULL;
|
|
return s->error_state = -1;
|
|
}
|
|
atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
|
|
}
|
|
*patom = atom;
|
|
return 0;
|
|
}
|
|
|
|
static int bc_get_atom(BCReaderState *s, JSAtom *patom)
|
|
{
|
|
uint32_t v;
|
|
if (bc_get_leb128(s, &v))
|
|
return -1;
|
|
if (v & 1) {
|
|
*patom = __JS_AtomFromUInt32(v >> 1);
|
|
return 0;
|
|
} else {
|
|
return bc_idx_to_atom(s, patom, v >> 1);
|
|
}
|
|
}
|
|
|
|
static JSString *JS_ReadString(BCReaderState *s)
|
|
{
|
|
uint32_t len;
|
|
size_t size;
|
|
BOOL is_wide_char;
|
|
JSString *p;
|
|
if (bc_get_leb128(s, &len))
|
|
return NULL;
|
|
is_wide_char = len & 1;
|
|
len >>= 1;
|
|
p = js_alloc_string(s->ctx, len, is_wide_char);
|
|
if (!p) {
|
|
s->error_state = -1;
|
|
return NULL;
|
|
}
|
|
size = (size_t)len << is_wide_char;
|
|
if ((s->buf_end - s->ptr) < size) {
|
|
bc_read_error_end(s);
|
|
js_free_string(s->ctx->rt, p);
|
|
return NULL;
|
|
}
|
|
memcpy(p->u.str8, s->ptr, size);
|
|
s->ptr += size;
|
|
if (!is_wide_char) {
|
|
p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
|
|
}
|
|
#ifdef DUMP_READ_OBJECT
|
|
JS_DumpString(s->ctx->rt, p); printf("\n");
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
|
|
{
|
|
uint32_t val;
|
|
/* XXX: this does not work for n == 32 */
|
|
val = (flags >> *pidx) & ((1U << n) - 1);
|
|
*pidx += n;
|
|
return val;
|
|
}
|
|
|
|
static JSBigFloat *js_new_bf(JSContext *ctx)
|
|
{
|
|
JSBigFloat *p;
|
|
p = js_malloc(ctx, sizeof(*p));
|
|
if (!p)
|
|
return NULL;
|
|
p->header.ref_count = 1;
|
|
bf_init(ctx->bf_ctx, &p->num);
|
|
return p;
|
|
}
|
|
|
|
#ifdef CONFIG_BIGNUM
|
|
static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
|
|
{
|
|
JSValue obj = JS_UNDEFINED;
|
|
uint8_t v8;
|
|
int32_t e;
|
|
uint32_t len;
|
|
limb_t l, i, n, j;
|
|
JSBigFloat *p;
|
|
limb_t v;
|
|
bf_t *a;
|
|
int bpos, d;
|
|
p = js_new_bf(s->ctx);
|
|
if (!p)
|
|
goto fail;
|
|
switch(tag) {
|
|
case BC_TAG_BIG_INT:
|
|
obj = JS_MKPTR(JS_TAG_BIG_INT, p);
|
|
break;
|
|
case BC_TAG_BIG_FLOAT:
|
|
obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
|
|
break;
|
|
case BC_TAG_BIG_DECIMAL:
|
|
obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
/* sign + exponent */
|
|
if (bc_get_sleb128(s, &e))
|
|
goto fail;
|
|
a = &p->num;
|
|
a->sign = e & 1;
|
|
e >>= 1;
|
|
if (e == 0)
|
|
a->expn = BF_EXP_ZERO;
|
|
else if (e == 1)
|
|
a->expn = BF_EXP_INF;
|
|
else if (e == 2)
|
|
a->expn = BF_EXP_NAN;
|
|
else if (e >= 3)
|
|
a->expn = e - 3;
|
|
else
|
|
a->expn = e;
|
|
/* mantissa */
|
|
if (a->expn != BF_EXP_ZERO &&
|
|
a->expn != BF_EXP_INF &&
|
|
a->expn != BF_EXP_NAN) {
|
|
if (bc_get_leb128(s, &len))
|
|
goto fail;
|
|
bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
|
|
if (len == 0) {
|
|
JS_ThrowInternalError(s->ctx, "invalid bignum length");
|
|
goto fail;
|
|
}
|
|
if (tag != BC_TAG_BIG_DECIMAL)
|
|
l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
|
|
else
|
|
l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
|
|
if (bf_resize(a, l)) {
|
|
JS_ThrowOutOfMemory(s->ctx);
|
|
goto fail;
|
|
}
|
|
if (tag != BC_TAG_BIG_DECIMAL) {
|
|
n = len & (sizeof(limb_t) - 1);
|
|
if (n != 0) {
|
|
v = 0;
|
|
for(i = 0; i < n; i++) {
|
|
if (bc_get_u8(s, &v8))
|
|
goto fail;
|
|
v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
|
|
}
|
|
a->tab[0] = v;
|
|
i = 1;
|
|
} else {
|
|
i = 0;
|
|
}
|
|
for(; i < l; i++) {
|
|
#if LIMB_BITS == 32
|
|
if (bc_get_u32(s, &v))
|
|
goto fail;
|
|
#ifdef WORDS_BIGENDIAN
|
|
v = bswap32(v);
|
|
#endif
|
|
#else
|
|
if (bc_get_u64(s, &v))
|
|
goto fail;
|
|
#ifdef WORDS_BIGENDIAN
|
|
v = bswap64(v);
|
|
#endif
|
|
#endif
|
|
a->tab[i] = v;
|
|
}
|
|
} else {
|
|
bpos = 0;
|
|
for(i = 0; i < l; i++) {
|
|
if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
|
|
j = LIMB_DIGITS - n;
|
|
} else {
|
|
j = 0;
|
|
}
|
|
v = 0;
|
|
for(; j < LIMB_DIGITS; j++) {
|
|
if (bpos == 0) {
|
|
if (bc_get_u8(s, &v8))
|
|
goto fail;
|
|
d = v8 & 0xf;
|
|
bpos = 1;
|
|
} else {
|
|
d = v8 >> 4;
|
|
bpos = 0;
|
|
}
|
|
if (d >= 10) {
|
|
JS_ThrowInternalError(s->ctx, "invalid digit");
|
|
goto fail;
|
|
}
|
|
v += mp_pow_dec[j] * d;
|
|
}
|
|
a->tab[i] = v;
|
|
}
|
|
}
|
|
}
|
|
bc_read_trace(s, "}\n");
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(s->ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
#endif /* CONFIG_BIGNUM */
|
|
|
|
static JSValue JS_ReadObjectRec(BCReaderState *s);
|
|
|
|
static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
|
|
{
|
|
if (s->allow_reference) {
|
|
if (js_resize_array(s->ctx, (void *)&s->objects,
|
|
sizeof(s->objects[0]),
|
|
&s->objects_size, s->objects_count + 1))
|
|
return -1;
|
|
s->objects[s->objects_count++] = p;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
|
|
{
|
|
return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
|
|
}
|
|
|
|
static JSValue JS_ReadFunctionTag(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
JSFunctionBytecode bc, *b;
|
|
JSValue obj = JS_UNDEFINED;
|
|
uint16_t v16;
|
|
uint8_t v8;
|
|
int idx, i, local_count;
|
|
int function_size, cpool_offset, byte_code_offset;
|
|
int closure_var_offset, vardefs_offset;
|
|
bzero(&bc, sizeof(bc));
|
|
bc.header.ref_count = 1;
|
|
//bc.gc_header.mark = 0;
|
|
if (bc_get_u16(s, &v16))
|
|
goto fail;
|
|
idx = 0;
|
|
bc.has_prototype = bc_get_flags(v16, &idx, 1);
|
|
bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
|
|
bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
|
|
bc.need_home_object = bc_get_flags(v16, &idx, 1);
|
|
bc.func_kind = bc_get_flags(v16, &idx, 2);
|
|
bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
|
|
bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
|
|
bc.super_allowed = bc_get_flags(v16, &idx, 1);
|
|
bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
|
|
bc.has_debug = bc_get_flags(v16, &idx, 1);
|
|
bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
|
|
bc.read_only_bytecode = s->is_rom_data;
|
|
if (bc_get_u8(s, &v8))
|
|
goto fail;
|
|
bc.js_mode = v8;
|
|
if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure
|
|
goto fail;
|
|
if (bc_get_leb128_u16(s, &bc.arg_count))
|
|
goto fail;
|
|
if (bc_get_leb128_u16(s, &bc.var_count))
|
|
goto fail;
|
|
if (bc_get_leb128_u16(s, &bc.defined_arg_count))
|
|
goto fail;
|
|
if (bc_get_leb128_u16(s, &bc.stack_size))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &bc.closure_var_count))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &bc.cpool_count))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &bc.byte_code_len))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &local_count))
|
|
goto fail;
|
|
if (bc.has_debug) {
|
|
function_size = sizeof(*b);
|
|
} else {
|
|
function_size = offsetof(JSFunctionBytecode, debug);
|
|
}
|
|
cpool_offset = function_size;
|
|
function_size += bc.cpool_count * sizeof(*bc.cpool);
|
|
vardefs_offset = function_size;
|
|
function_size += local_count * sizeof(*bc.vardefs);
|
|
closure_var_offset = function_size;
|
|
function_size += bc.closure_var_count * sizeof(*bc.closure_var);
|
|
byte_code_offset = function_size;
|
|
if (!bc.read_only_bytecode) {
|
|
function_size += bc.byte_code_len;
|
|
}
|
|
b = js_mallocz(ctx, function_size);
|
|
if (!b)
|
|
return JS_EXCEPTION;
|
|
memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
|
|
b->header.ref_count = 1;
|
|
if (local_count != 0) {
|
|
b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
|
|
}
|
|
if (b->closure_var_count != 0) {
|
|
b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
|
|
}
|
|
if (b->cpool_count != 0) {
|
|
b->cpool = (void *)((uint8_t*)b + cpool_offset);
|
|
}
|
|
add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
|
|
obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
|
|
#ifdef DUMP_READ_OBJECT
|
|
bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
|
|
#endif
|
|
bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
|
|
b->arg_count, b->var_count, b->defined_arg_count,
|
|
b->closure_var_count, b->cpool_count);
|
|
bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
|
|
b->stack_size, b->byte_code_len, local_count);
|
|
if (local_count != 0) {
|
|
bc_read_trace(s, "vars {\n");
|
|
for(i = 0; i < local_count; i++) {
|
|
JSVarDef *vd = &b->vardefs[i];
|
|
if (bc_get_atom(s, &vd->var_name))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &vd->scope_level))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &vd->scope_next))
|
|
goto fail;
|
|
vd->scope_next--;
|
|
if (bc_get_u8(s, &v8))
|
|
goto fail;
|
|
idx = 0;
|
|
vd->var_kind = bc_get_flags(v8, &idx, 4);
|
|
vd->is_const = bc_get_flags(v8, &idx, 1);
|
|
vd->is_lexical = bc_get_flags(v8, &idx, 1);
|
|
vd->is_captured = bc_get_flags(v8, &idx, 1);
|
|
#ifdef DUMP_READ_OBJECT
|
|
bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
|
|
#endif
|
|
}
|
|
bc_read_trace(s, "}\n");
|
|
}
|
|
if (b->closure_var_count != 0) {
|
|
bc_read_trace(s, "closure vars {\n");
|
|
for(i = 0; i < b->closure_var_count; i++) {
|
|
JSClosureVar *cv = &b->closure_var[i];
|
|
int var_idx;
|
|
if (bc_get_atom(s, &cv->var_name))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &var_idx))
|
|
goto fail;
|
|
cv->var_idx = var_idx;
|
|
if (bc_get_u8(s, &v8))
|
|
goto fail;
|
|
idx = 0;
|
|
cv->is_local = bc_get_flags(v8, &idx, 1);
|
|
cv->is_arg = bc_get_flags(v8, &idx, 1);
|
|
cv->is_const = bc_get_flags(v8, &idx, 1);
|
|
cv->is_lexical = bc_get_flags(v8, &idx, 1);
|
|
cv->var_kind = bc_get_flags(v8, &idx, 4);
|
|
#ifdef DUMP_READ_OBJECT
|
|
bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
|
|
#endif
|
|
}
|
|
bc_read_trace(s, "}\n");
|
|
}
|
|
{
|
|
bc_read_trace(s, "bytecode {\n");
|
|
if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
|
|
goto fail;
|
|
bc_read_trace(s, "}\n");
|
|
}
|
|
if (b->has_debug) {
|
|
/* read optional debug information */
|
|
bc_read_trace(s, "debug {\n");
|
|
if (bc_get_atom(s, &b->debug.filename))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &b->debug.line_num))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &b->debug.pc2line_len))
|
|
goto fail;
|
|
if (b->debug.pc2line_len) {
|
|
b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
|
|
if (!b->debug.pc2line_buf)
|
|
goto fail;
|
|
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
|
|
bc_read_trace(s, "}\n");
|
|
}
|
|
if (b->cpool_count != 0) {
|
|
bc_read_trace(s, "cpool {\n");
|
|
for(i = 0; i < b->cpool_count; i++) {
|
|
JSValue val;
|
|
val = JS_ReadObjectRec(s);
|
|
if (JS_IsException(val))
|
|
goto fail;
|
|
b->cpool[i] = val;
|
|
}
|
|
bc_read_trace(s, "}\n");
|
|
}
|
|
b->realm = JS_DupContext(ctx);
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadModule(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
JSValue obj;
|
|
JSModuleDef *m = NULL;
|
|
JSAtom module_name;
|
|
int i;
|
|
uint8_t v8;
|
|
if (bc_get_atom(s, &module_name))
|
|
goto fail;
|
|
#ifdef DUMP_READ_OBJECT
|
|
bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
|
|
#endif
|
|
m = js_new_module_def(ctx, module_name);
|
|
if (!m)
|
|
goto fail;
|
|
obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
|
|
if (bc_get_leb128_int(s, &m->req_module_entries_count))
|
|
goto fail;
|
|
if (m->req_module_entries_count != 0) {
|
|
m->req_module_entries_size = m->req_module_entries_count;
|
|
m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
|
|
if (!m->req_module_entries)
|
|
goto fail;
|
|
for(i = 0; i < m->req_module_entries_count; i++) {
|
|
JSReqModuleEntry *rme = &m->req_module_entries[i];
|
|
if (bc_get_atom(s, &rme->module_name))
|
|
goto fail;
|
|
}
|
|
}
|
|
if (bc_get_leb128_int(s, &m->export_entries_count))
|
|
goto fail;
|
|
if (m->export_entries_count != 0) {
|
|
m->export_entries_size = m->export_entries_count;
|
|
m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
|
|
if (!m->export_entries)
|
|
goto fail;
|
|
for(i = 0; i < m->export_entries_count; i++) {
|
|
JSExportEntry *me = &m->export_entries[i];
|
|
if (bc_get_u8(s, &v8))
|
|
goto fail;
|
|
me->export_type = v8;
|
|
if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
|
|
if (bc_get_leb128_int(s, &me->u.local.var_idx))
|
|
goto fail;
|
|
} else {
|
|
if (bc_get_leb128_int(s, &me->u.req_module_idx))
|
|
goto fail;
|
|
if (bc_get_atom(s, &me->local_name))
|
|
goto fail;
|
|
}
|
|
if (bc_get_atom(s, &me->export_name))
|
|
goto fail;
|
|
}
|
|
}
|
|
if (bc_get_leb128_int(s, &m->star_export_entries_count))
|
|
goto fail;
|
|
if (m->star_export_entries_count != 0) {
|
|
m->star_export_entries_size = m->star_export_entries_count;
|
|
m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
|
|
if (!m->star_export_entries)
|
|
goto fail;
|
|
for(i = 0; i < m->star_export_entries_count; i++) {
|
|
JSStarExportEntry *se = &m->star_export_entries[i];
|
|
if (bc_get_leb128_int(s, &se->req_module_idx))
|
|
goto fail;
|
|
}
|
|
}
|
|
if (bc_get_leb128_int(s, &m->import_entries_count))
|
|
goto fail;
|
|
if (m->import_entries_count != 0) {
|
|
m->import_entries_size = m->import_entries_count;
|
|
m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
|
|
if (!m->import_entries)
|
|
goto fail;
|
|
for(i = 0; i < m->import_entries_count; i++) {
|
|
JSImportEntry *mi = &m->import_entries[i];
|
|
if (bc_get_leb128_int(s, &mi->var_idx))
|
|
goto fail;
|
|
if (bc_get_atom(s, &mi->import_name))
|
|
goto fail;
|
|
if (bc_get_leb128_int(s, &mi->req_module_idx))
|
|
goto fail;
|
|
}
|
|
}
|
|
m->func_obj = JS_ReadObjectRec(s);
|
|
if (JS_IsException(m->func_obj))
|
|
goto fail;
|
|
return obj;
|
|
fail:
|
|
if (m) {
|
|
js_free_module_def(ctx, m);
|
|
}
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadObjectTag(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
JSValue obj;
|
|
uint32_t prop_count, i;
|
|
JSAtom atom;
|
|
JSValue val;
|
|
int ret;
|
|
obj = JS_NewObject(ctx);
|
|
if (BC_add_object_ref(s, obj))
|
|
goto fail;
|
|
if (bc_get_leb128(s, &prop_count))
|
|
goto fail;
|
|
for(i = 0; i < prop_count; i++) {
|
|
if (bc_get_atom(s, &atom))
|
|
goto fail;
|
|
#ifdef DUMP_READ_OBJECT
|
|
bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
|
|
#endif
|
|
val = JS_ReadObjectRec(s);
|
|
if (JS_IsException(val)) {
|
|
JS_FreeAtom(ctx, atom);
|
|
goto fail;
|
|
}
|
|
ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
|
|
JS_FreeAtom(ctx, atom);
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadArray(BCReaderState *s, int tag)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
JSValue obj;
|
|
uint32_t len, i;
|
|
JSValue val;
|
|
int ret, prop_flags;
|
|
BOOL is_template;
|
|
obj = JS_NewArray(ctx);
|
|
if (BC_add_object_ref(s, obj))
|
|
goto fail;
|
|
is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
|
|
if (bc_get_leb128(s, &len))
|
|
goto fail;
|
|
for(i = 0; i < len; i++) {
|
|
val = JS_ReadObjectRec(s);
|
|
if (JS_IsException(val))
|
|
goto fail;
|
|
if (is_template)
|
|
prop_flags = JS_PROP_ENUMERABLE;
|
|
else
|
|
prop_flags = JS_PROP_C_W_E;
|
|
ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
|
|
prop_flags);
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
if (is_template) {
|
|
val = JS_ReadObjectRec(s);
|
|
if (JS_IsException(val))
|
|
goto fail;
|
|
if (!JS_IsUndefined(val)) {
|
|
ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
JS_PreventExtensions(ctx, obj);
|
|
}
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadTypedArray(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
|
|
uint8_t array_tag;
|
|
JSValueConst args[3];
|
|
uint32_t offset, len, idx;
|
|
if (bc_get_u8(s, &array_tag))
|
|
return JS_EXCEPTION;
|
|
if (array_tag >= JS_TYPED_ARRAY_COUNT)
|
|
return JS_ThrowTypeError(ctx, "invalid typed array");
|
|
if (bc_get_leb128(s, &len))
|
|
return JS_EXCEPTION;
|
|
if (bc_get_leb128(s, &offset))
|
|
return JS_EXCEPTION;
|
|
/* XXX: this hack could be avoided if the typed array could be
|
|
created before the array buffer */
|
|
idx = s->objects_count;
|
|
if (BC_add_object_ref1(s, NULL))
|
|
goto fail;
|
|
array_buffer = JS_ReadObjectRec(s);
|
|
if (JS_IsException(array_buffer))
|
|
return JS_EXCEPTION;
|
|
if (!js_get_array_buffer(ctx, array_buffer)) {
|
|
JS_FreeValue(ctx, array_buffer);
|
|
return JS_EXCEPTION;
|
|
}
|
|
args[0] = array_buffer;
|
|
args[1] = JS_NewInt64(ctx, offset);
|
|
args[2] = JS_NewInt64(ctx, len);
|
|
obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
|
|
3, args,
|
|
JS_CLASS_UINT8C_ARRAY + array_tag);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
if (s->allow_reference) {
|
|
s->objects[idx] = JS_VALUE_GET_OBJ(obj);
|
|
}
|
|
JS_FreeValue(ctx, array_buffer);
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, array_buffer);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadArrayBuffer(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
uint32_t byte_length;
|
|
JSValue obj;
|
|
|
|
if (bc_get_leb128(s, &byte_length))
|
|
return JS_EXCEPTION;
|
|
if (UNLIKELY(s->buf_end - s->ptr < byte_length)) {
|
|
bc_read_error_end(s);
|
|
return JS_EXCEPTION;
|
|
}
|
|
obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
if (BC_add_object_ref(s, obj))
|
|
goto fail;
|
|
s->ptr += byte_length;
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
uint32_t byte_length;
|
|
uint8_t *data_ptr;
|
|
JSValue obj;
|
|
uint64_t u64;
|
|
if (bc_get_leb128(s, &byte_length))
|
|
return JS_EXCEPTION;
|
|
if (bc_get_u64(s, &u64))
|
|
return JS_EXCEPTION;
|
|
data_ptr = (uint8_t *)(uintptr_t)u64;
|
|
/* the SharedArrayBuffer is cloned */
|
|
obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
|
|
JS_CLASS_SHARED_ARRAY_BUFFER,
|
|
data_ptr,
|
|
NULL, NULL, FALSE);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
if (BC_add_object_ref(s, obj))
|
|
goto fail;
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadDate(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
JSValue val, obj = JS_UNDEFINED;
|
|
val = JS_ReadObjectRec(s);
|
|
if (JS_IsException(val))
|
|
goto fail;
|
|
if (!JS_IsNumber(val)) {
|
|
JS_ThrowTypeError(ctx, "Number tag expected for date");
|
|
goto fail;
|
|
}
|
|
obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
|
|
JS_CLASS_DATE);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
if (BC_add_object_ref(s, obj))
|
|
goto fail;
|
|
JS_SetObjectData(ctx, obj, val);
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadObjectValue(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
JSValue val, obj = JS_UNDEFINED;
|
|
val = JS_ReadObjectRec(s);
|
|
if (JS_IsException(val))
|
|
goto fail;
|
|
obj = JS_ToObject(ctx, val);
|
|
if (JS_IsException(obj))
|
|
goto fail;
|
|
if (BC_add_object_ref(s, obj))
|
|
goto fail;
|
|
JS_FreeValue(ctx, val);
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue JS_ReadObjectRec(BCReaderState *s)
|
|
{
|
|
JSContext *ctx = s->ctx;
|
|
uint8_t tag;
|
|
JSValue obj = JS_UNDEFINED;
|
|
if (js_check_stack_overflow(ctx->rt, 0))
|
|
return JS_ThrowStackOverflow(ctx);
|
|
if (bc_get_u8(s, &tag))
|
|
return JS_EXCEPTION;
|
|
bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
|
|
switch(tag) {
|
|
case BC_TAG_NULL:
|
|
obj = JS_NULL;
|
|
break;
|
|
case BC_TAG_UNDEFINED:
|
|
obj = JS_UNDEFINED;
|
|
break;
|
|
case BC_TAG_BOOL_FALSE:
|
|
case BC_TAG_BOOL_TRUE:
|
|
obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
|
|
break;
|
|
case BC_TAG_INT32:
|
|
{
|
|
int32_t val;
|
|
if (bc_get_sleb128(s, &val))
|
|
return JS_EXCEPTION;
|
|
bc_read_trace(s, "%d\n", val);
|
|
obj = JS_NewInt32(ctx, val);
|
|
}
|
|
break;
|
|
case BC_TAG_FLOAT64:
|
|
{
|
|
JSFloat64Union u;
|
|
if (bc_get_u64(s, &u.u64))
|
|
return JS_EXCEPTION;
|
|
bc_read_trace(s, "%g\n", u.d);
|
|
obj = __JS_NewFloat64(ctx, u.d);
|
|
}
|
|
break;
|
|
case BC_TAG_STRING:
|
|
{
|
|
JSString *p;
|
|
p = JS_ReadString(s);
|
|
if (!p)
|
|
return JS_EXCEPTION;
|
|
obj = JS_MKPTR(JS_TAG_STRING, p);
|
|
}
|
|
break;
|
|
case BC_TAG_FUNCTION_BYTECODE:
|
|
if (!s->allow_bytecode)
|
|
goto invalid_tag;
|
|
obj = JS_ReadFunctionTag(s);
|
|
break;
|
|
case BC_TAG_MODULE:
|
|
if (!s->allow_bytecode)
|
|
goto invalid_tag;
|
|
obj = JS_ReadModule(s);
|
|
break;
|
|
case BC_TAG_OBJECT:
|
|
obj = JS_ReadObjectTag(s);
|
|
break;
|
|
case BC_TAG_ARRAY:
|
|
case BC_TAG_TEMPLATE_OBJECT:
|
|
obj = JS_ReadArray(s, tag);
|
|
break;
|
|
case BC_TAG_TYPED_ARRAY:
|
|
obj = JS_ReadTypedArray(s);
|
|
break;
|
|
case BC_TAG_ARRAY_BUFFER:
|
|
obj = JS_ReadArrayBuffer(s);
|
|
break;
|
|
case BC_TAG_SHARED_ARRAY_BUFFER:
|
|
if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
|
|
goto invalid_tag;
|
|
obj = JS_ReadSharedArrayBuffer(s);
|
|
break;
|
|
case BC_TAG_DATE:
|
|
obj = JS_ReadDate(s);
|
|
break;
|
|
case BC_TAG_OBJECT_VALUE:
|
|
obj = JS_ReadObjectValue(s);
|
|
break;
|
|
#ifdef CONFIG_BIGNUM
|
|
case BC_TAG_BIG_INT:
|
|
case BC_TAG_BIG_FLOAT:
|
|
case BC_TAG_BIG_DECIMAL:
|
|
obj = JS_ReadBigNum(s, tag);
|
|
break;
|
|
#endif
|
|
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;
|
|
bzero(s, 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;
|
|
}
|