mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 15:03:34 +00:00
- 10.5% reduction of o//depend dependency graph - 8.8% reduction in latency of make command - Fix issue with temporary file cleanup There's a new -w option in compile.com that turns off the recent Landlock output path workaround for "good commands" which do not unlink() the output file like GNU tooling does. Our new GNU Make unveil sandboxing appears to have zero overhead in the grand scheme of things. Full builds are pretty fast since the only thing that's actually slowed us down is probably libcxx make -j16 MODE=rel RL: took 85,732,063µs wall time RL: ballooned to 323,612kb in size RL: needed 828,560,521µs cpu (11% kernel) RL: caused 39,080,670 page faults (99% memcpy) RL: 350,073 context switches (72% consensual) RL: performed 0 reads and 11,494,960 write i/o operations pledge() and unveil() no longer consider ENOSYS to be an error. These functions have also been added to Python's cosmo module. This change also removes some WIN32 APIs and System Five magnums which we're not using and it's doubtful anyone else would be too
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;
|
|
}
|