cosmopolitan/third_party/quickjs/byte.c

2241 lines
65 KiB
C
Raw Normal View History

/*
* 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)
{
Make numerous improvements - Python static hello world now 1.8mb - Python static fully loaded now 10mb - Python HTTPS client now uses MbedTLS - Python REPL now completes import stmts - Increase stack size for Python for now - Begin synthesizing posixpath and ntpath - Restore Python \N{UNICODE NAME} support - Restore Python NFKD symbol normalization - Add optimized code path for Intel SHA-NI - Get more Python unit tests passing faster - Get Python help() pagination working on NT - Python hashlib now supports MbedTLS PBKDF2 - Make memcpy/memmove/memcmp/bcmp/etc. faster - Add Mersenne Twister and Vigna to LIBC_RAND - Provide privileged __printf() for error code - Fix zipos opendir() so that it reports ENOTDIR - Add basic chmod() implementation for Windows NT - Add Cosmo's best functions to Python cosmo module - Pin function trace indent depth to that of caller - Show memory diagram on invalid access in MODE=dbg - Differentiate stack overflow on crash in MODE=dbg - Add stb_truetype and tools for analyzing font files - Upgrade to UNICODE 13 and reduce its binary footprint - COMPILE.COM now logs resource usage of build commands - Start implementing basic poll() support on bare metal - Set getauxval(AT_EXECFN) to GetModuleFileName() on NT - Add descriptions to strerror() in non-TINY build modes - Add COUNTBRANCH() macro to help with micro-optimizations - Make error / backtrace / asan / memory code more unbreakable - Add fast perfect C implementation of μ-Law and a-Law audio codecs - Make strtol() functions consistent with other libc implementations - Improve Linenoise implementation (see also github.com/jart/bestline) - COMPILE.COM now suppresses stdout/stderr of successful build commands
2021-09-28 05:58:51 +00:00
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;
Make numerous improvements - Python static hello world now 1.8mb - Python static fully loaded now 10mb - Python HTTPS client now uses MbedTLS - Python REPL now completes import stmts - Increase stack size for Python for now - Begin synthesizing posixpath and ntpath - Restore Python \N{UNICODE NAME} support - Restore Python NFKD symbol normalization - Add optimized code path for Intel SHA-NI - Get more Python unit tests passing faster - Get Python help() pagination working on NT - Python hashlib now supports MbedTLS PBKDF2 - Make memcpy/memmove/memcmp/bcmp/etc. faster - Add Mersenne Twister and Vigna to LIBC_RAND - Provide privileged __printf() for error code - Fix zipos opendir() so that it reports ENOTDIR - Add basic chmod() implementation for Windows NT - Add Cosmo's best functions to Python cosmo module - Pin function trace indent depth to that of caller - Show memory diagram on invalid access in MODE=dbg - Differentiate stack overflow on crash in MODE=dbg - Add stb_truetype and tools for analyzing font files - Upgrade to UNICODE 13 and reduce its binary footprint - COMPILE.COM now logs resource usage of build commands - Start implementing basic poll() support on bare metal - Set getauxval(AT_EXECFN) to GetModuleFileName() on NT - Add descriptions to strerror() in non-TINY build modes - Add COUNTBRANCH() macro to help with micro-optimizations - Make error / backtrace / asan / memory code more unbreakable - Add fast perfect C implementation of μ-Law and a-Law audio codecs - Make strtol() functions consistent with other libc implementations - Improve Linenoise implementation (see also github.com/jart/bestline) - COMPILE.COM now suppresses stdout/stderr of successful build commands
2021-09-28 05:58:51 +00:00
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;
Make numerous improvements - Python static hello world now 1.8mb - Python static fully loaded now 10mb - Python HTTPS client now uses MbedTLS - Python REPL now completes import stmts - Increase stack size for Python for now - Begin synthesizing posixpath and ntpath - Restore Python \N{UNICODE NAME} support - Restore Python NFKD symbol normalization - Add optimized code path for Intel SHA-NI - Get more Python unit tests passing faster - Get Python help() pagination working on NT - Python hashlib now supports MbedTLS PBKDF2 - Make memcpy/memmove/memcmp/bcmp/etc. faster - Add Mersenne Twister and Vigna to LIBC_RAND - Provide privileged __printf() for error code - Fix zipos opendir() so that it reports ENOTDIR - Add basic chmod() implementation for Windows NT - Add Cosmo's best functions to Python cosmo module - Pin function trace indent depth to that of caller - Show memory diagram on invalid access in MODE=dbg - Differentiate stack overflow on crash in MODE=dbg - Add stb_truetype and tools for analyzing font files - Upgrade to UNICODE 13 and reduce its binary footprint - COMPILE.COM now logs resource usage of build commands - Start implementing basic poll() support on bare metal - Set getauxval(AT_EXECFN) to GetModuleFileName() on NT - Add descriptions to strerror() in non-TINY build modes - Add COUNTBRANCH() macro to help with micro-optimizations - Make error / backtrace / asan / memory code more unbreakable - Add fast perfect C implementation of μ-Law and a-Law audio codecs - Make strtol() functions consistent with other libc implementations - Improve Linenoise implementation (see also github.com/jart/bestline) - COMPILE.COM now suppresses stdout/stderr of successful build commands
2021-09-28 05:58:51 +00:00
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;
Make numerous improvements - Python static hello world now 1.8mb - Python static fully loaded now 10mb - Python HTTPS client now uses MbedTLS - Python REPL now completes import stmts - Increase stack size for Python for now - Begin synthesizing posixpath and ntpath - Restore Python \N{UNICODE NAME} support - Restore Python NFKD symbol normalization - Add optimized code path for Intel SHA-NI - Get more Python unit tests passing faster - Get Python help() pagination working on NT - Python hashlib now supports MbedTLS PBKDF2 - Make memcpy/memmove/memcmp/bcmp/etc. faster - Add Mersenne Twister and Vigna to LIBC_RAND - Provide privileged __printf() for error code - Fix zipos opendir() so that it reports ENOTDIR - Add basic chmod() implementation for Windows NT - Add Cosmo's best functions to Python cosmo module - Pin function trace indent depth to that of caller - Show memory diagram on invalid access in MODE=dbg - Differentiate stack overflow on crash in MODE=dbg - Add stb_truetype and tools for analyzing font files - Upgrade to UNICODE 13 and reduce its binary footprint - COMPILE.COM now logs resource usage of build commands - Start implementing basic poll() support on bare metal - Set getauxval(AT_EXECFN) to GetModuleFileName() on NT - Add descriptions to strerror() in non-TINY build modes - Add COUNTBRANCH() macro to help with micro-optimizations - Make error / backtrace / asan / memory code more unbreakable - Add fast perfect C implementation of μ-Law and a-Law audio codecs - Make strtol() functions consistent with other libc implementations - Improve Linenoise implementation (see also github.com/jart/bestline) - COMPILE.COM now suppresses stdout/stderr of successful build commands
2021-09-28 05:58:51 +00:00
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;
}