Unofficial mirror of Fabrice Bellard's quickjs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

53313 lines
1.7 MiB

/*
* QuickJS Javascript Engine
*
* Copyright (c) 2017-2020 Fabrice Bellard
* Copyright (c) 2017-2020 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 <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <sys/time.h>
#include <time.h>
#include <fenv.h>
#include <math.h>
#if defined(__APPLE__)
#include <malloc/malloc.h>
#elif defined(__linux__)
#include <malloc.h>
#endif
#include "cutils.h"
#include "list.h"
#include "quickjs.h"
#include "libregexp.h"
#ifdef CONFIG_BIGNUM
#include "libbf.h"
#endif
#define OPTIMIZE 1
#define SHORT_OPCODES 1
#if defined(EMSCRIPTEN)
#define DIRECT_DISPATCH 0
#else
#define DIRECT_DISPATCH 1
#endif
#if defined(__APPLE__)
#define MALLOC_OVERHEAD 0
#else
#define MALLOC_OVERHEAD 8
#endif
#if !defined(_WIN32)
/* define it if printf uses the RNDN rounding mode instead of RNDNA */
#define CONFIG_PRINTF_RNDN
#endif
/* define to include Atomics.* operations which depend on the OS
threads */
#if !defined(EMSCRIPTEN)
#define CONFIG_ATOMICS
#endif
#if !defined(EMSCRIPTEN)
/* enable stack limitation */
#define CONFIG_STACK_CHECK
#endif
/* dump object free */
//#define DUMP_FREE
//#define DUMP_CLOSURE
/* dump the bytecode of the compiled functions: combination of bits
1: dump pass 3 final byte code
2: dump pass 2 code
4: dump pass 1 code
8: dump stdlib functions
16: dump bytecode in hex
32: dump line number table
*/
//#define DUMP_BYTECODE (1)
/* dump the occurence of the automatic GC */
//#define DUMP_GC
/* dump objects freed by the garbage collector */
//#define DUMP_GC_FREE
/* dump objects leaking when freeing the runtime */
//#define DUMP_LEAKS 1
/* dump memory usage before running the garbage collector */
//#define DUMP_MEM
//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */
//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */
//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */
//#define DUMP_MODULE_RESOLVE
//#define DUMP_PROMISE
//#define DUMP_READ_OBJECT
/* test the GC by forcing it before each object allocation */
//#define FORCE_GC_AT_MALLOC
#ifdef CONFIG_ATOMICS
#include <pthread.h>
#include <stdatomic.h>
#include <errno.h>
#endif
enum {
/* classid tag */ /* union usage | properties */
JS_CLASS_OBJECT = 1, /* must be first */
JS_CLASS_ARRAY, /* u.array | length */
JS_CLASS_ERROR,
JS_CLASS_NUMBER, /* u.object_data */
JS_CLASS_STRING, /* u.object_data */
JS_CLASS_BOOLEAN, /* u.object_data */
JS_CLASS_SYMBOL, /* u.object_data */
JS_CLASS_ARGUMENTS, /* u.array | length */
JS_CLASS_MAPPED_ARGUMENTS, /* | length */
JS_CLASS_DATE, /* u.object_data */
JS_CLASS_MODULE_NS,
JS_CLASS_C_FUNCTION, /* u.cfunc */
JS_CLASS_BYTECODE_FUNCTION, /* u.func */
JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
JS_CLASS_GENERATOR_FUNCTION, /* u.func */
JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
JS_CLASS_REGEXP, /* u.regexp */
JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */
JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */
JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */
JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */
JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */
JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */
JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */
JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */
#ifdef CONFIG_BIGNUM
JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */
JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */
#endif
JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */
JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */
JS_CLASS_DATAVIEW, /* u.typed_array */
#ifdef CONFIG_BIGNUM
JS_CLASS_BIG_INT, /* u.object_data */
JS_CLASS_BIG_FLOAT, /* u.object_data */
JS_CLASS_FLOAT_ENV, /* u.float_env */
JS_CLASS_BIG_DECIMAL, /* u.object_data */
JS_CLASS_OPERATOR_SET, /* u.operator_set */
#endif
JS_CLASS_MAP, /* u.map_state */
JS_CLASS_SET, /* u.map_state */
JS_CLASS_WEAKMAP, /* u.map_state */
JS_CLASS_WEAKSET, /* u.map_state */
JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */
JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */
JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */
JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */
JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
JS_CLASS_GENERATOR, /* u.generator_data */
JS_CLASS_PROXY, /* u.proxy_data */
JS_CLASS_PROMISE, /* u.promise_data */
JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */
JS_CLASS_ASYNC_FUNCTION, /* u.func */
JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */
JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */
JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
};
/* number of typed array types */
#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
typedef enum JSErrorEnum {
JS_EVAL_ERROR,
JS_RANGE_ERROR,
JS_REFERENCE_ERROR,
JS_SYNTAX_ERROR,
JS_TYPE_ERROR,
JS_URI_ERROR,
JS_INTERNAL_ERROR,
JS_AGGREGATE_ERROR,
JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
} JSErrorEnum;
#define JS_MAX_LOCAL_VARS 65536
#define JS_STACK_SIZE_MAX 65536
#define JS_STRING_LEN_MAX ((1 << 30) - 1)
#define __exception __attribute__((warn_unused_result))
typedef struct JSShape JSShape;
typedef struct JSString JSString;
typedef struct JSString JSAtomStruct;
typedef enum {
JS_GC_PHASE_NONE,
JS_GC_PHASE_DECREF,
JS_GC_PHASE_REMOVE_CYCLES,
} JSGCPhaseEnum;
typedef enum OPCodeEnum OPCodeEnum;
#ifdef CONFIG_BIGNUM
/* function pointers are used for numeric operations so that it is
possible to remove some numeric types */
typedef struct {
JSValue (*to_string)(JSContext *ctx, JSValueConst val);
JSValue (*from_string)(JSContext *ctx, const char *buf,
int radix, int flags, slimb_t *pexponent);
int (*unary_arith)(JSContext *ctx,
JSValue *pres, OPCodeEnum op, JSValue op1);
int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
JSValue *pres, JSValue op1, JSValue op2);
int (*compare)(JSContext *ctx, OPCodeEnum op,
JSValue op1, JSValue op2);
/* only for bigfloat: */
JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
int64_t exponent);
int (*mul_pow10)(JSContext *ctx, JSValue *sp);
} JSNumericOperations;
#endif
struct JSRuntime {
JSMallocFunctions mf;
JSMallocState malloc_state;
const char *rt_info;
int atom_hash_size; /* power of two */
int atom_count;
int atom_size;
int atom_count_resize; /* resize hash table at this count */
uint32_t *atom_hash;
JSAtomStruct **atom_array;
int atom_free_index; /* 0 = none */
int class_count; /* size of class_array */
JSClass *class_array;
struct list_head context_list; /* list of JSContext.link */
/* list of JSGCObjectHeader.link. List of allocated GC objects (used
by the garbage collector) */
struct list_head gc_obj_list;
/* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
struct list_head gc_zero_ref_count_list;
struct list_head tmp_obj_list; /* used during GC */
JSGCPhaseEnum gc_phase : 8;
size_t malloc_gc_threshold;
#ifdef DUMP_LEAKS
struct list_head string_list; /* list of JSString.link */
#endif
/* stack limitation */
const uint8_t *stack_top;
size_t stack_size; /* in bytes */
JSValue current_exception;
/* true if inside an out of memory error, to avoid recursing */
BOOL in_out_of_memory : 8;
struct JSStackFrame *current_stack_frame;
JSInterruptHandler *interrupt_handler;
void *interrupt_opaque;
JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
void *host_promise_rejection_tracker_opaque;
struct list_head job_list; /* list of JSJobEntry.link */
JSModuleNormalizeFunc *module_normalize_func;
JSModuleLoaderFunc *module_loader_func;
void *module_loader_opaque;
BOOL can_block : 8; /* TRUE if Atomics.wait can block */
/* used to allocate, free and clone SharedArrayBuffers */
JSSharedArrayBufferFunctions sab_funcs;
/* Shape hash table */
int shape_hash_bits;
int shape_hash_size;
int shape_hash_count; /* number of hashed shapes */
JSShape **shape_hash;
#ifdef CONFIG_BIGNUM
bf_context_t bf_ctx;
JSNumericOperations bigint_ops;
JSNumericOperations bigfloat_ops;
JSNumericOperations bigdecimal_ops;
uint32_t operator_count;
#endif
void *user_opaque;
};
struct JSClass {
uint32_t class_id; /* 0 means free entry */
JSAtom class_name;
JSClassFinalizer *finalizer;
JSClassGCMark *gc_mark;
JSClassCall *call;
/* pointers for exotic behavior, can be NULL if none are present */
const JSClassExoticMethods *exotic;
};
#define JS_MODE_STRICT (1 << 0)
#define JS_MODE_STRIP (1 << 1)
#define JS_MODE_MATH (1 << 2)
typedef struct JSStackFrame {
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
JSValue *arg_buf; /* arguments */
JSValue *var_buf; /* variables */
struct list_head var_ref_list; /* list of JSVarRef.link */
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
instruction after the call */
int arg_count;
int js_mode; /* 0 or JS_MODE_MATH for C functions */
/* only used in generators. Current stack pointer value. NULL if
the function is running. */
JSValue *cur_sp;
} JSStackFrame;
typedef enum {
JS_GC_OBJ_TYPE_JS_OBJECT,
JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
JS_GC_OBJ_TYPE_SHAPE,
JS_GC_OBJ_TYPE_VAR_REF,
JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
JS_GC_OBJ_TYPE_JS_CONTEXT,
} JSGCObjectTypeEnum;
/* header for GC objects. GC objects are C data structures with a
reference count that can reference other GC objects. JS Objects are
a particular type of GC object. */
struct JSGCObjectHeader {
int ref_count; /* must come first, 32-bit */
JSGCObjectTypeEnum gc_obj_type : 4;
uint8_t mark : 4; /* used by the GC */
uint8_t dummy1; /* not used by the GC */
uint16_t dummy2; /* not used by the GC */
struct list_head link;
};
typedef struct JSVarRef {
union {
JSGCObjectHeader header; /* must come first */
struct {
int __gc_ref_count; /* corresponds to header.ref_count */
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
/* 0 : the JSVarRef is on the stack. header.link is an element
of JSStackFrame.var_ref_list.
1 : the JSVarRef is detached. header.link has the normal meanning
*/
uint8_t is_detached : 1;
uint8_t is_arg : 1;
uint16_t var_idx; /* index of the corresponding function variable on
the stack */
};
};
JSValue *pvalue; /* pointer to the value, either on the stack or
to 'value' */
JSValue value; /* used when the variable is no longer on the stack */
} JSVarRef;
#ifdef CONFIG_BIGNUM
typedef struct JSFloatEnv {
limb_t prec;
bf_flags_t flags;
unsigned int status;
} JSFloatEnv;
/* the same structure is used for big integers and big floats. Big
integers are never infinite or NaNs */
typedef struct JSBigFloat {
JSRefCountHeader header; /* must come first, 32-bit */
bf_t num;
} JSBigFloat;
typedef struct JSBigDecimal {
JSRefCountHeader header; /* must come first, 32-bit */
bfdec_t num;
} JSBigDecimal;
#endif
typedef enum {
JS_AUTOINIT_ID_PROTOTYPE,
JS_AUTOINIT_ID_MODULE_NS,
JS_AUTOINIT_ID_PROP,
} JSAutoInitIDEnum;
/* must be large enough to have a negligible runtime cost and small
enough to call the interrupt callback often. */
#define JS_INTERRUPT_COUNTER_INIT 10000
struct JSContext {
JSGCObjectHeader header; /* must come first */
JSRuntime *rt;
struct list_head link;
uint16_t binary_object_count;
int binary_object_size;
JSShape *array_shape; /* initial shape for Array objects */
JSValue *class_proto;
JSValue function_proto;
JSValue function_ctor;
JSValue array_ctor;
JSValue regexp_ctor;
JSValue promise_ctor;
JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
JSValue iterator_proto;
JSValue async_iterator_proto;
JSValue array_proto_values;
JSValue throw_type_error;
JSValue eval_obj;
JSValue global_obj; /* global object */
JSValue global_var_obj; /* contains the global let/const definitions */
uint64_t random_state;
#ifdef CONFIG_BIGNUM
bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */
JSFloatEnv fp_env; /* global FP environment */
BOOL bignum_ext : 8; /* enable math mode */
BOOL allow_operator_overloading : 8;
#endif
/* when the counter reaches zero, JSRutime.interrupt_handler is called */
int interrupt_counter;
BOOL is_error_property_enabled;
struct list_head loaded_modules; /* list of JSModuleDef.link */
/* if NULL, RegExp compilation is not supported */
JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,
JSValueConst flags);
/* if NULL, eval is not supported */
JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
const char *input, size_t input_len,
const char *filename, int flags, int scope_idx);
void *user_opaque;
};
typedef union JSFloat64Union {
double d;
uint64_t u64;
uint32_t u32[2];
} JSFloat64Union;
enum {
JS_ATOM_TYPE_STRING = 1,
JS_ATOM_TYPE_GLOBAL_SYMBOL,
JS_ATOM_TYPE_SYMBOL,
JS_ATOM_TYPE_PRIVATE,
};
enum {
JS_ATOM_HASH_SYMBOL,
JS_ATOM_HASH_PRIVATE,
};
typedef enum {
JS_ATOM_KIND_STRING,
JS_ATOM_KIND_SYMBOL,
JS_ATOM_KIND_PRIVATE,
} JSAtomKindEnum;
#define JS_ATOM_HASH_MASK ((1 << 30) - 1)
struct JSString {
JSRefCountHeader header; /* must come first, 32-bit */
uint32_t len : 31;
uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
/* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,
for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3
XXX: could change encoding to have one more bit in hash */
uint32_t hash : 30;
uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
#ifdef DUMP_LEAKS
struct list_head link; /* string list */
#endif
union {
uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */
uint16_t str16[0];
} u;
};
typedef struct JSClosureVar {
uint8_t is_local : 1;
uint8_t is_arg : 1;
uint8_t is_const : 1;
uint8_t is_lexical : 1;
uint8_t var_kind : 3; /* see JSVarKindEnum */
/* 9 bits available */
uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
parent function. otherwise: index to a closure
variable of the parent function */
JSAtom var_name;
} JSClosureVar;
typedef struct JSVarScope {
int parent; /* index into fd->scopes of the enclosing scope */
int first; /* index into fd->vars of the last variable in this scope */
} JSVarScope;
typedef enum {
/* XXX: add more variable kinds here instead of using bit fields */
JS_VAR_NORMAL,
JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */
JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator
function declaration */
JS_VAR_CATCH,
JS_VAR_PRIVATE_FIELD,
JS_VAR_PRIVATE_METHOD,
JS_VAR_PRIVATE_GETTER,
JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
} JSVarKindEnum;
typedef struct JSVarDef {
JSAtom var_name;
int scope_level; /* index into fd->scopes of this variable lexical scope */
int scope_next; /* index into fd->vars of the next variable in the
* same or enclosing lexical scope */
uint8_t is_func_var : 1; /* used for the function self reference */
uint8_t is_const : 1;
uint8_t is_lexical : 1;
uint8_t is_captured : 1;
uint8_t var_kind : 4; /* see JSVarKindEnum */
/* only used during compilation: function pool index for lexical
variables with var_kind =
JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
the definition of the 'var' variables (they have scope_level =
0) */
int func_pool_or_scope_idx : 24; /* only used during compilation */
} JSVarDef;
/* for the encoding of the pc2line table */
#define PC2LINE_BASE (-1)
#define PC2LINE_RANGE 5
#define PC2LINE_OP_FIRST 1
#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
typedef enum JSFunctionKindEnum {
JS_FUNC_NORMAL = 0,
JS_FUNC_GENERATOR = (1 << 0),
JS_FUNC_ASYNC = (1 << 1),
JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),
} JSFunctionKindEnum;
typedef struct JSFunctionBytecode {
JSGCObjectHeader header; /* must come first */
uint8_t js_mode;
uint8_t has_prototype : 1; /* true if a prototype field is necessary */
uint8_t has_simple_parameter_list : 1;
uint8_t is_derived_class_constructor : 1;
/* true if home_object needs to be initialized */
uint8_t need_home_object : 1;
uint8_t func_kind : 2;
uint8_t new_target_allowed : 1;
uint8_t super_call_allowed : 1;
uint8_t super_allowed : 1;
uint8_t arguments_allowed : 1;
uint8_t has_debug : 1;
uint8_t backtrace_barrier : 1; /* stop backtrace on this function */
uint8_t read_only_bytecode : 1;
/* XXX: 4 bits available */
uint8_t *byte_code_buf; /* (self pointer) */
int byte_code_len;
JSAtom func_name;
JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
uint16_t arg_count;
uint16_t var_count;
uint16_t defined_arg_count; /* for length function property */
uint16_t stack_size; /* maximum stack size */
JSContext *realm; /* function realm */
JSValue *cpool; /* constant pool (self pointer) */
int cpool_count;
int closure_var_count;
struct {
/* debug info, move to separate structure to save memory? */
JSAtom filename;
int line_num;
int source_len;
int pc2line_len;
uint8_t *pc2line_buf;
char *source;
} debug;
} JSFunctionBytecode;
typedef struct JSBoundFunction {
JSValue func_obj;
JSValue this_val;
int argc;
JSValue argv[0];
} JSBoundFunction;
typedef enum JSIteratorKindEnum {
JS_ITERATOR_KIND_KEY,
JS_ITERATOR_KIND_VALUE,
JS_ITERATOR_KIND_KEY_AND_VALUE,
} JSIteratorKindEnum;
typedef struct JSForInIterator {
JSValue obj;
BOOL is_array;
uint32_t array_length;
uint32_t idx;
} JSForInIterator;
typedef struct JSRegExp {
JSString *pattern;
JSString *bytecode; /* also contains the flags */
} JSRegExp;
typedef struct JSProxyData {
JSValue target;
JSValue handler;
uint8_t is_func;
uint8_t is_revoked;
} JSProxyData;
typedef struct JSArrayBuffer {
int byte_length; /* 0 if detached */
uint8_t detached;
uint8_t shared; /* if shared, the array buffer cannot be detached */
uint8_t *data; /* NULL if detached */
struct list_head array_list;
void *opaque;
JSFreeArrayBufferDataFunc *free_func;
} JSArrayBuffer;
typedef struct JSTypedArray {
struct list_head link; /* link to arraybuffer */
JSObject *obj; /* back pointer to the TypedArray/DataView object */
JSObject *buffer; /* based array buffer */
uint32_t offset; /* offset in the array buffer */
uint32_t length; /* length in the array buffer */
} JSTypedArray;
typedef struct JSAsyncFunctionState {
JSValue this_val; /* 'this' generator argument */
int argc; /* number of function arguments */
BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
JSStackFrame frame;
} JSAsyncFunctionState;
/* XXX: could use an object instead to avoid the
JS_TAG_ASYNC_FUNCTION tag for the GC */
typedef struct JSAsyncFunctionData {
JSGCObjectHeader header; /* must come first */
JSValue resolving_funcs[2];
BOOL is_active; /* true if the async function state is valid */
JSAsyncFunctionState func_state;
} JSAsyncFunctionData;
typedef enum {
/* binary operators */
JS_OVOP_ADD,
JS_OVOP_SUB,
JS_OVOP_MUL,
JS_OVOP_DIV,
JS_OVOP_MOD,
JS_OVOP_POW,
JS_OVOP_OR,
JS_OVOP_AND,
JS_OVOP_XOR,
JS_OVOP_SHL,
JS_OVOP_SAR,
JS_OVOP_SHR,
JS_OVOP_EQ,
JS_OVOP_LESS,
JS_OVOP_BINARY_COUNT,
/* unary operators */
JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
JS_OVOP_NEG,
JS_OVOP_INC,
JS_OVOP_DEC,
JS_OVOP_NOT,
JS_OVOP_COUNT,
} JSOverloadableOperatorEnum;
typedef struct {
uint32_t operator_index;
JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
} JSBinaryOperatorDefEntry;
typedef struct {
int count;
JSBinaryOperatorDefEntry *tab;
} JSBinaryOperatorDef;
typedef struct {
uint32_t operator_counter;
BOOL is_primitive; /* OperatorSet for a primitive type */
/* NULL if no operator is defined */
JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
JSBinaryOperatorDef left;
JSBinaryOperatorDef right;
} JSOperatorSetData;
typedef struct JSReqModuleEntry {
JSAtom module_name;
JSModuleDef *module; /* used using resolution */
} JSReqModuleEntry;
typedef enum JSExportTypeEnum {
JS_EXPORT_TYPE_LOCAL,
JS_EXPORT_TYPE_INDIRECT,
} JSExportTypeEnum;
typedef struct JSExportEntry {
union {
struct {
int var_idx; /* closure variable index */
JSVarRef *var_ref; /* if != NULL, reference to the variable */
} local; /* for local export */
int req_module_idx; /* module for indirect export */
} u;
JSExportTypeEnum export_type;
JSAtom local_name; /* '*' if export ns from. not used for local
export after compilation */
JSAtom export_name; /* exported variable name */
} JSExportEntry;
typedef struct JSStarExportEntry {
int req_module_idx; /* in req_module_entries */
} JSStarExportEntry;
typedef struct JSImportEntry {
int var_idx; /* closure variable index */
JSAtom import_name;
int req_module_idx; /* in req_module_entries */
} JSImportEntry;
struct JSModuleDef {
JSRefCountHeader header; /* must come first, 32-bit */
JSAtom module_name;
struct list_head link;
JSReqModuleEntry *req_module_entries;
int req_module_entries_count;
int req_module_entries_size;
JSExportEntry *export_entries;
int export_entries_count;
int export_entries_size;
JSStarExportEntry *star_export_entries;
int star_export_entries_count;
int star_export_entries_size;
JSImportEntry *import_entries;
int import_entries_count;
int import_entries_size;
JSValue module_ns;
JSValue func_obj; /* only used for JS modules */
JSModuleInitFunc *init_func; /* only used for C modules */
BOOL resolved : 8;
BOOL func_created : 8;
BOOL instantiated : 8;
BOOL evaluated : 8;
BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */
/* true if evaluation yielded an exception. It is saved in
eval_exception */
BOOL eval_has_exception : 8;
JSValue eval_exception;
JSValue meta_obj; /* for import.meta */
};
typedef struct JSJobEntry {
struct list_head link;
JSContext *ctx;
JSJobFunc *job_func;
int argc;
JSValue argv[0];
} JSJobEntry;
typedef struct JSProperty {
union {
JSValue value; /* JS_PROP_NORMAL */
struct { /* JS_PROP_GETSET */
JSObject *getter; /* NULL if undefined */
JSObject *setter; /* NULL if undefined */
} getset;
JSVarRef *var_ref; /* JS_PROP_VARREF */
struct { /* JS_PROP_AUTOINIT */
/* in order to use only 2 pointers, we compress the realm
and the init function pointer */
uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
in the 2 low bits */
void *opaque;
} init;
} u;
} JSProperty;
#define JS_PROP_INITIAL_SIZE 2
#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
#define JS_ARRAY_INITIAL_SIZE 2
typedef struct JSShapeProperty {
uint32_t hash_next : 26; /* 0 if last in list */
uint32_t flags : 6; /* JS_PROP_XXX */
JSAtom atom; /* JS_ATOM_NULL = free property entry */
} JSShapeProperty;
struct JSShape {
uint32_t prop_hash_end[0]; /* hash table of size hash_mask + 1
before the start of the structure. */
JSGCObjectHeader header;
/* true if the shape is inserted in the shape hash table. If not,
JSShape.hash is not valid */
uint8_t is_hashed;
/* If true, the shape may have small array index properties 'n' with 0
<= n <= 2^31-1. If false, the shape is guaranteed not to have
small array index properties */
uint8_t has_small_array_index;
uint32_t hash; /* current hash value */
uint32_t prop_hash_mask;
int prop_size; /* allocated properties */
int prop_count; /* include deleted properties */
int deleted_prop_count;
JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
JSObject *proto;
JSShapeProperty prop[0]; /* prop_size elements */
};
struct JSObject {
union {
JSGCObjectHeader header;
struct {
int __gc_ref_count; /* corresponds to header.ref_count */
uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
uint8_t extensible : 1;
uint8_t free_mark : 1; /* only used when freeing objects with cycles */
uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
uint8_t fast_array : 1; /* TRUE if u.array is used for get/put */
uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
uint16_t class_id; /* see JS_CLASS_x */
};
};
/* byte offsets: 16/24 */
JSShape *shape; /* prototype and property names + flag */
JSProperty *prop; /* array of properties */
/* byte offsets: 24/40 */
struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */
/* byte offsets: 28/48 */
union {
void *opaque;
struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
#ifdef CONFIG_BIGNUM
struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
#endif
struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
/* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
struct JSFunctionBytecode *function_bytecode;
JSVarRef **var_refs;
JSObject *home_object; /* for 'super' access */
} func;
struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
JSContext *realm;
JSCFunctionType c_function;
uint8_t length;
uint8_t cproto;
int16_t magic;
} cfunc;
/* array part for fast arrays and typed arrays */
struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
union {
uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
} u1;
union {
JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */
uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */
uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */
int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */
uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */
int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */
uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */
float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */
double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */
} u;
uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
} array; /* 12/20 bytes */
JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */
JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */
} u;
/* byte sizes: 40/48/72 */
};
enum {
JS_ATOM_NULL,
#define DEF(name, str) JS_ATOM_ ## name,
#include "quickjs-atom.h"
#undef DEF
JS_ATOM_END,
};
#define JS_ATOM_LAST_KEYWORD JS_ATOM_super
#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
static const char js_atom_init[] =
#define DEF(name, str) str "\0"
#include "quickjs-atom.h"
#undef DEF
;
typedef enum OPCodeFormat {
#define FMT(f) OP_FMT_ ## f,
#define DEF(id, size, n_pop, n_push, f)
#include "quickjs-opcode.h"
#undef DEF
#undef FMT
} OPCodeFormat;
enum OPCodeEnum {
#define FMT(f)
#define DEF(id, size, n_pop, n_push, f) OP_ ## id,
#define def(id, size, n_pop, n_push, f)
#include "quickjs-opcode.h"
#undef def
#undef DEF
#undef FMT
OP_COUNT, /* excluding temporary opcodes */
/* temporary opcodes : overlap with the short opcodes */
OP_TEMP_START = OP_nop + 1,
OP___dummy = OP_TEMP_START - 1,
#define FMT(f)
#define DEF(id, size, n_pop, n_push, f)
#define def(id, size, n_pop, n_push, f) OP_ ## id,
#include "quickjs-opcode.h"
#undef def
#undef DEF
#undef FMT
OP_TEMP_END,
};
static int JS_InitAtoms(JSRuntime *rt);
static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
int atom_type);
static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_obj,
int argc, JSValueConst *argv, int flags);
static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_obj,
int argc, JSValueConst *argv, int flags);
static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_obj, JSValueConst new_target,
int argc, JSValue *argv, int flags);
static JSValue JS_CallConstructorInternal(JSContext *ctx,
JSValueConst func_obj,
JSValueConst new_target,
int argc, JSValue *argv, int flags);
static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
int argc, JSValueConst *argv);
static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
int argc, JSValueConst *argv);
static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
JSValue val);
static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
JSValueConst val, int flags, int scope_idx);
JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
static __maybe_unused void JS_DumpString(JSRuntime *rt,
const JSString *p);
static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
JSValueConst val);
static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
static __maybe_unused void JS_PrintValue(JSContext *ctx,
const char *str,
JSValueConst val);
static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic);
static void js_array_finalizer(JSRuntime *rt, JSValue val);
static void js_array_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_map_finalizer(JSRuntime *rt, JSValue val);
static void js_map_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
static void js_generator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_promise_finalizer(JSRuntime *rt, JSValue val);
static void js_promise_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
#ifdef CONFIG_BIGNUM
static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
#endif
static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
static int JS_ToBoolFree(JSContext *ctx, JSValue val);
static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
JSValueConst flags);
static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
JSValue pattern, JSValue bc);
static void gc_decref(JSRuntime *rt);
static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
const JSClassDef *class_def, JSAtom name);
typedef enum JSStrictEqModeEnum {
JS_EQ_STRICT,
JS_EQ_SAME_VALUE,
JS_EQ_SAME_VALUE_ZERO,
} JSStrictEqModeEnum;
static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
JSStrictEqModeEnum eq_mode);
static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);
static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
static JSProperty *add_property(JSContext *ctx,
JSObject *p, JSAtom prop, int prop_flags);
#ifdef CONFIG_BIGNUM
static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
static JSValue JS_NewBigFloat(JSContext *ctx);
static inline bf_t *JS_GetBigFloat(JSValueConst val)
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
return &p->num;
}
static JSValue JS_NewBigDecimal(JSContext *ctx);
static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
{
JSBigDecimal *p = JS_VALUE_GET_PTR(val);
return &p->num;
}
static JSValue JS_NewBigInt(JSContext *ctx);
static inline bf_t *JS_GetBigInt(JSValueConst val)
{
JSBigFloat *p = JS_VALUE_GET_PTR(val);
return &p->num;
}
static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
BOOL convert_to_safe_integer);
static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
BOOL allow_null_or_undefined);
static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
#endif
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
JSValueConst proto_val, BOOL throw_flag);
static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);
static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);
static int JS_CreateProperty(JSContext *ctx, JSObject *p,
JSAtom prop, JSValueConst val,
JSValueConst getter, JSValueConst setter,
int flags);
static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
static void reset_weak_ref(JSRuntime *rt, JSObject *p);
static JSValue js_array_buffer_constructor3(JSContext *ctx,
JSValueConst new_target,
uint64_t len, JSClassID class_id,
uint8_t *buf,
JSFreeArrayBufferDataFunc *free_func,
void *opaque, BOOL alloc_flag);
static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
static JSValue js_typed_array_constructor(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv,
int classid);
static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
BOOL is_arg);
static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_obj,
int argc, JSValueConst *argv,
int flags);
static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
const char *input, size_t input_len,
const char *filename, int flags, int scope_idx);
static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
JS_MarkFunc *mark_func);
static JSValue js_import_meta(JSContext *ctx);
static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);
static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
static JSValue js_new_promise_capability(JSContext *ctx,
JSValue *resolving_funcs,
JSValueConst ctor);
static __exception int perform_promise_then(JSContext *ctx,
JSValueConst promise,
JSValueConst *resolve_reject,
JSValueConst *cap_resolving_funcs);
static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic);
static int js_string_compare(JSContext *ctx,
const JSString *p1, const JSString *p2);
static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
JSValue prop, JSValue val, int flags);
static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
JSObject *p, JSAtom prop);
static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
JS_MarkFunc *mark_func);
static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
static void js_free_shape(JSRuntime *rt, JSShape *sh);
static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
JSShapeProperty **pprs);
static int init_shape_hash(JSRuntime *rt);
static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
JSValueConst obj);
static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
JSValueConst obj);
static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
JSValueConst array_arg);
static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
JSValue **arrpp, uint32_t *countp);
static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
JSValueConst sync_iter);
static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
JSValueConst this_val,
int argc, JSValueConst *argv, int flags);
static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
JSGCObjectTypeEnum type);
static void remove_gc_object(JSGCObjectHeader *h);
static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);
static int js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
static int js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
void *opaque);
static int JS_InstantiateFunctionListItem(JSContext *ctx, JSObject *p,
JSAtom atom, void *opaque);
void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
static const JSClassExoticMethods js_arguments_exotic_methods;
static const JSClassExoticMethods js_string_exotic_methods;
static const JSClassExoticMethods js_proxy_exotic_methods;
static const JSClassExoticMethods js_module_ns_exotic_methods;
static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
static void js_trigger_gc(JSRuntime *rt, size_t size)
{
BOOL force_gc;
#ifdef FORCE_GC_AT_MALLOC
force_gc = TRUE;
#else
force_gc = ((rt->malloc_state.malloc_size + size) >
rt->malloc_gc_threshold);
#endif
if (force_gc) {
#ifdef DUMP_GC
printf("GC: size=%" PRIu64 "\n",
(uint64_t)rt->malloc_state.malloc_size);
#endif
JS_RunGC(rt);
rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
(rt->malloc_state.malloc_size >> 1);
}
}
static size_t js_malloc_usable_size_unknown(const void *ptr)
{
return 0;
}
void *js_malloc_rt(JSRuntime *rt, size_t size)
{
return rt->mf.js_malloc(&rt->malloc_state, size);
}
void js_free_rt(JSRuntime *rt, void *ptr)
{
rt->mf.js_free(&rt->malloc_state, ptr);
}
void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
{
return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
}
size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
{
return rt->mf.js_malloc_usable_size(ptr);
}
void *js_mallocz_rt(JSRuntime *rt, size_t size)
{
void *ptr;
ptr = js_malloc_rt(rt, size);
if (!ptr)
return NULL;
return memset(ptr, 0, size);
}
#ifdef CONFIG_BIGNUM
/* called by libbf */
static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
{
JSRuntime *rt = opaque;
return js_realloc_rt(rt, ptr, size);
}
#endif /* CONFIG_BIGNUM */
/* Throw out of memory in case of error */
void *js_malloc(JSContext *ctx, size_t size)
{
void *ptr;
ptr = js_malloc_rt(ctx->rt, size);
if (unlikely(!ptr)) {
JS_ThrowOutOfMemory(ctx);
return NULL;
}
return ptr;
}
/* Throw out of memory in case of error */
void *js_mallocz(JSContext *ctx, size_t size)
{
void *ptr;
ptr = js_mallocz_rt(ctx->rt, size);
if (unlikely(!ptr)) {
JS_ThrowOutOfMemory(ctx);
return NULL;
}
return ptr;
}
void js_free(JSContext *ctx, void *ptr)
{
js_free_rt(ctx->rt, ptr);
}
/* Throw out of memory in case of error */
void *js_realloc(JSContext *ctx, void *ptr, size_t size)
{
void *ret;
ret = js_realloc_rt(ctx->rt, ptr, size);
if (unlikely(!ret && size != 0)) {
JS_ThrowOutOfMemory(ctx);
return NULL;
}
return ret;
}
/* store extra allocated size in *pslack if successful */
void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
{
void *ret;
ret = js_realloc_rt(ctx->rt, ptr, size);
if (unlikely(!ret && size != 0)) {
JS_ThrowOutOfMemory(ctx);
return NULL;
}
if (pslack) {
size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
*pslack = (new_size > size) ? new_size - size : 0;
}
return ret;
}
size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
{
return js_malloc_usable_size_rt(ctx->rt, ptr);
}
/* Throw out of memory exception in case of error */
char *js_strndup(JSContext *ctx, const char *s, size_t n)
{
char *ptr;
ptr = js_malloc(ctx, n + 1);
if (ptr) {
memcpy(ptr, s, n);
ptr[n] = '\0';
}
return ptr;
}
char *js_strdup(JSContext *ctx, const char *str)
{
return js_strndup(ctx, str, strlen(str));
}
static no_inline int js_realloc_array(JSContext *ctx, void **parray,
int elem_size, int *psize, int req_size)
{
int new_size;
size_t slack;
void *new_array;
/* XXX: potential arithmetic overflow */
new_size = max_int(req_size, *psize * 3 / 2);
new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
if (!new_array)
return -1;
new_size += slack / elem_size;
*psize = new_size;
*parray = new_array;
return 0;
}
/* resize the array and update its size if req_size > *psize */
static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
int *psize, int req_size)
{
if (unlikely(req_size > *psize))
return js_realloc_array(ctx, parray, elem_size, psize, req_size);
else
return 0;
}
static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
{
dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
}
static inline int is_digit(int c) {
return c >= '0' && c <= '9';
}
typedef struct JSClassShortDef {
JSAtom class_name;
JSClassFinalizer *finalizer;
JSClassGCMark *gc_mark;
} JSClassShortDef;
static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */
{ JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */
{ JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
{ JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
{ JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
{ JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
{ JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
{ JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */
{ JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */
{ JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
{ JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */
{ JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
{ JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
{ JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
{ JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
{ JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */
{ JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
{ JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
{ JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */
{ JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */
{ JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
{ JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */
{ JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */
{ JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */
{ JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */
{ JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */
{ JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */
#ifdef CONFIG_BIGNUM
{ JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */
{ JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */
#endif
{ JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */
{ JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */
{ JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */
#ifdef CONFIG_BIGNUM
{ JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
{ JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */
{ JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */
{ JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */
{ JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */
#endif
{ JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */
{ JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */
{ JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */
{ JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */
{ JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
{ JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
{ JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
{ JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
{ JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
{ JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
};
static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
int start, int count)
{
JSClassDef cm_s, *cm = &cm_s;
int i, class_id;
for(i = 0; i < count; i++) {
class_id = i + start;
memset(cm, 0, sizeof(*cm));
cm->finalizer = tab[i].finalizer;
cm->gc_mark = tab[i].gc_mark;
if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
return -1;
}
return 0;
}
#ifdef CONFIG_BIGNUM
static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
{
return JS_ThrowTypeError(ctx, "unsupported operation");
}
static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
{
return JS_ThrowUnsupportedOperation(ctx);
}
static JSValue invalid_from_string(JSContext *ctx, const char *buf,
int radix, int flags, slimb_t *pexponent)
{
return JS_NAN;
}
static int invalid_unary_arith(JSContext *ctx,
JSValue *pres, OPCodeEnum op, JSValue op1)
{
JS_FreeValue(ctx, op1);
JS_ThrowUnsupportedOperation(ctx);
return -1;
}
static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
JSValue *pres, JSValue op1, JSValue op2)
{
JS_FreeValue(ctx, op1);
JS_FreeValue(ctx, op2);
JS_ThrowUnsupportedOperation(ctx);
return -1;
}
static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
int64_t exponent)
{
return JS_ThrowUnsupportedOperation(ctx);
}
static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
{
JS_ThrowUnsupportedOperation(ctx);
return -1;
}
static void set_dummy_numeric_ops(JSNumericOperations *ops)
{
ops->to_string = invalid_to_string;
ops->from_string = invalid_from_string;
ops->unary_arith = invalid_unary_arith;
ops->binary_arith = invalid_binary_arith;
ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
ops->mul_pow10 = invalid_mul_pow10;
}
#endif /* CONFIG_BIGNUM */
#if !defined(CONFIG_STACK_CHECK)
/* no stack limitation */
static inline uint8_t *js_get_stack_pointer(void)
{
return NULL;
}
static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
{
return FALSE;
}
#else
/* Note: OS and CPU dependent */
static inline uint8_t *js_get_stack_pointer(void)
{
return __builtin_frame_address(0);
}
static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
{
size_t size;
size = rt->stack_top - js_get_stack_pointer();
return unlikely((size + alloca_size) > rt->stack_size);
}
#endif
JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
{
JSRuntime *rt;
JSMallocState ms;
memset(&ms, 0, sizeof(ms));
ms.opaque = opaque;
ms.malloc_limit = -1;
rt = mf->js_malloc(&ms, sizeof(JSRuntime));
if (!rt)
return NULL;
memset(rt, 0, sizeof(*rt));
rt->mf = *mf;
if (!rt->mf.js_malloc_usable_size) {
/* use dummy function if none provided */
rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
}
rt->malloc_state = ms;
rt->malloc_gc_threshold = 256 * 1024;
#ifdef CONFIG_BIGNUM
bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
set_dummy_numeric_ops(&rt->bigint_ops);
set_dummy_numeric_ops(&rt->bigfloat_ops);
set_dummy_numeric_ops(&rt->bigdecimal_ops);
#endif
init_list_head(&rt->context_list);
init_list_head(&rt->gc_obj_list);
init_list_head(&rt->gc_zero_ref_count_list);
rt->gc_phase = JS_GC_PHASE_NONE;
#ifdef DUMP_LEAKS
init_list_head(&rt->string_list);
#endif
init_list_head(&rt->job_list);
if (JS_InitAtoms(rt))
goto fail;
/* create the object, array and function classes */
if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
countof(js_std_class_def)) < 0)
goto fail;
rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
if (init_shape_hash(rt))
goto fail;
rt->stack_top = js_get_stack_pointer();
rt->stack_size = JS_DEFAULT_STACK_SIZE;
rt->current_exception = JS_NULL;
return rt;
fail:
JS_FreeRuntime(rt);
return NULL;
}
void *JS_GetRuntimeOpaque(JSRuntime *rt)
{
return rt->user_opaque;
}
void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
{
rt->user_opaque = opaque;
}
/* default memory allocation functions with memory limitation */
static inline size_t js_def_malloc_usable_size(void *ptr)
{
#if defined(__APPLE__)
return malloc_size(ptr);
#elif defined(_WIN32)
return _msize(ptr);
#elif defined(EMSCRIPTEN)
return 0;
#elif defined(__linux__)
return malloc_usable_size(ptr);
#else
/* change this to `return 0;` if compilation fails */
return malloc_usable_size(ptr);
#endif
}
static void *js_def_malloc(JSMallocState *s, size_t size)
{
void *ptr;
/* Do not allocate zero bytes: behavior is platform dependent */
assert(size != 0);
if (unlikely(s->malloc_size + size > s->malloc_limit))
return NULL;
ptr = malloc(size);
if (!ptr)
return NULL;
s->malloc_count++;
s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
return ptr;
}
static void js_def_free(JSMallocState *s, void *ptr)
{
if (!ptr)
return;
s->malloc_count--;
s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
free(ptr);
}
static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
{
size_t old_size;
if (!ptr) {
if (size == 0)
return NULL;
return js_def_malloc(s, size);
}
old_size = js_def_malloc_usable_size(ptr);
if (size == 0) {
s->malloc_count--;
s->malloc_size -= old_size + MALLOC_OVERHEAD;
free(ptr);
return NULL;
}
if (s->malloc_size + size - old_size > s->malloc_limit)
return NULL;
ptr = realloc(ptr, size);
if (!ptr)
return NULL;
s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
return ptr;
}
static const JSMallocFunctions def_malloc_funcs = {
js_def_malloc,
js_def_free,
js_def_realloc,
#if defined(__APPLE__)
malloc_size,
#elif defined(_WIN32)
(size_t (*)(const void *))_msize,
#elif defined(EMSCRIPTEN)
NULL,
#elif defined(__linux__)
(size_t (*)(const void *))malloc_usable_size,
#else
/* change this to `NULL,` if compilation fails */
malloc_usable_size,
#endif
};
JSRuntime *JS_NewRuntime(void)
{
return JS_NewRuntime2(&def_malloc_funcs, NULL);
}
void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
{
rt->malloc_state.malloc_limit = limit;
}
/* use -1 to disable automatic GC */
void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
{
rt->malloc_gc_threshold = gc_threshold;
}
#define malloc(s) malloc_is_forbidden(s)
#define free(p) free_is_forbidden(p)
#define realloc(p,s) realloc_is_forbidden(p,s)
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
{
rt->interrupt_handler = cb;
rt->interrupt_opaque = opaque;
}
void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
{
rt->can_block = can_block;
}
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
const JSSharedArrayBufferFunctions *sf)
{
rt->sab_funcs = *sf;
}
/* return 0 if OK, < 0 if exception */
int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
int argc, JSValueConst *argv)
{
JSRuntime *rt = ctx->rt;
JSJobEntry *e;
int i;
e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
if (!e)
return -1;
e->ctx = ctx;
e->job_func = job_func;
e->argc = argc;
for(i = 0; i < argc; i++) {
e->argv[i] = JS_DupValue(ctx, argv[i]);
}
list_add_tail(&e->link, &rt->job_list);
return 0;
}
BOOL JS_IsJobPending(JSRuntime *rt)
{
return !list_empty(&rt->job_list);
}
/* return < 0 if exception, 0 if no job pending, 1 if a job was
executed successfully. the context of the job is stored in '*pctx' */
int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
{
JSContext *ctx;
JSJobEntry *e;
JSValue res;
int i, ret;
if (list_empty(&rt->job_list)) {
*pctx = NULL;
return 0;
}
/* get the first pending job and execute it */
e = list_entry(rt->job_list.next, JSJobEntry, link);
list_del(&e->link);
ctx = e->ctx;
res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
for(i = 0; i < e->argc; i++)
JS_FreeValue(ctx, e->argv[i]);
if (JS_IsException(res))
ret = -1;
else
ret = 1;
JS_FreeValue(ctx, res);
js_free(ctx, e);
*pctx = ctx;
return ret;
}
static inline uint32_t atom_get_free(const JSAtomStruct *p)
{
return (uintptr_t)p >> 1;
}
static inline BOOL atom_is_free(const JSAtomStruct *p)
{
return (uintptr_t)p & 1;
}
static inline JSAtomStruct *atom_set_free(uint32_t v)
{
return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
}
/* Note: the string contents are uninitialized */
static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
{
JSString *str;
str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
if (unlikely(!str))
return NULL;
str->header.ref_count = 1;
str->is_wide_char = is_wide_char;
str->len = max_len;
str->atom_type = 0;
str->hash = 0; /* optional but costless */
str->hash_next = 0; /* optional */
#ifdef DUMP_LEAKS
list_add_tail(&str->link, &rt->string_list);
#endif
return str;
}
static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
{
JSString *p;
p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
if (unlikely(!p)) {
JS_ThrowOutOfMemory(ctx);
return NULL;
}
return p;
}
/* same as JS_FreeValueRT() but faster */
static inline void js_free_string(JSRuntime *rt, JSString *str)
{
if (--str->header.ref_count <= 0) {
if (str->atom_type) {
JS_FreeAtomStruct(rt, str);
} else {
#ifdef DUMP_LEAKS
list_del(&str->link);
#endif
js_free_rt(rt, str);
}
}
}
void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
{
if (rt)
rt->rt_info = s;
}
void JS_FreeRuntime(JSRuntime *rt)
{
struct list_head *el, *el1;
int i;
JS_FreeValueRT(rt, rt->current_exception);
list_for_each_safe(el, el1, &rt->job_list) {
JSJobEntry *e = list_entry(el, JSJobEntry, link);
for(i = 0; i < e->argc; i++)
JS_FreeValueRT(rt, e->argv[i]);
js_free_rt(rt, e);
}
init_list_head(&rt->job_list);
JS_RunGC(rt);
#ifdef DUMP_LEAKS
/* leaking objects */
{
BOOL header_done;
JSGCObjectHeader *p;
int count;
/* remove the internal refcounts to display only the object
referenced externally */
list_for_each(el, &rt->gc_obj_list) {
p = list_entry(el, JSGCObjectHeader, link);
p->mark = 0;
}
gc_decref(rt);
header_done = FALSE;
list_for_each(el, &rt->gc_obj_list) {
p = list_entry(el, JSGCObjectHeader, link);
if (p->ref_count != 0) {
if (!header_done) {
printf("Object leaks:\n");
JS_DumpObjectHeader(rt);
header_done = TRUE;
}
JS_DumpGCObject(rt, p);
}
}
count = 0;
list_for_each(el, &rt->gc_obj_list) {
p = list_entry(el, JSGCObjectHeader, link);
if (p->ref_count == 0) {
count++;
}
}
if (count != 0)
printf("Secondary object leaks: %d\n", count);
}
#endif
assert(list_empty(&rt->gc_obj_list));
/* free the classes */
for(i = 0; i < rt->class_count; i++) {
JSClass *cl = &rt->class_array[i];
if (cl->class_id != 0) {
JS_FreeAtomRT(rt, cl->class_name);
}
}
js_free_rt(rt, rt->class_array);
#ifdef CONFIG_BIGNUM
bf_context_end(&rt->bf_ctx);
#endif
#ifdef DUMP_LEAKS
/* only the atoms defined in JS_InitAtoms() should be left */
{
BOOL header_done = FALSE;
for(i = 0; i < rt->atom_size; i++) {
JSAtomStruct *p = rt->atom_array[i];
if (!atom_is_free(p) /* && p->str*/) {
if (i >= JS_ATOM_END || p->header.ref_count != 1) {
if (!header_done) {
header_done = TRUE;
if (rt->rt_info) {
printf("%s:1: atom leakage:", rt->rt_info);
} else {
printf("Atom leaks:\n"
" %6s %6s %s\n",
"ID", "REFCNT", "NAME");
}
}
if (rt->rt_info) {
printf(" ");
} else {
printf(" %6u %6u ", i, p->header.ref_count);
}
switch (p->atom_type) {
case JS_ATOM_TYPE_STRING:
JS_DumpString(rt, p);
break;
case JS_ATOM_TYPE_GLOBAL_SYMBOL:
printf("Symbol.for(");
JS_DumpString(rt, p);
printf(")");
break;
case JS_ATOM_TYPE_SYMBOL:
if (p->hash == JS_ATOM_HASH_SYMBOL) {
printf("Symbol(");
JS_DumpString(rt, p);
printf(")");
} else {
printf("Private(");
JS_DumpString(rt, p);
printf(")");
}
break;
}
if (rt->rt_info) {
printf(":%u", p->header.ref_count);
} else {
printf("\n");
}
}
}
}
if (rt->rt_info && header_done)
printf("\n");
}
#endif
/* free the atoms */
for(i = 0; i < rt->atom_size; i++) {
JSAtomStruct *p = rt->atom_array[i];
if (!atom_is_free(p)) {
#ifdef DUMP_LEAKS
list_del(&p->link);
#endif
js_free_rt(rt, p);
}
}
js_free_rt(rt, rt->atom_array);
js_free_rt(rt, rt->atom_hash);
js_free_rt(rt, rt->shape_hash);
#ifdef DUMP_LEAKS
if (!list_empty(&rt->string_list)) {
if (rt->rt_info) {
printf("%s:1: string leakage:", rt->rt_info);
} else {
printf("String leaks:\n"
" %6s %s\n",
"REFCNT", "VALUE");
}
list_for_each_safe(el, el1, &rt->string_list) {
JSString *str = list_entry(el, JSString, link);
if (rt->rt_info) {
printf(" ");
} else {
printf(" %6u ", str->header.ref_count);
}
JS_DumpString(rt, str);
if (rt->rt_info) {
printf(":%u", str->header.ref_count);
} else {
printf("\n");
}
list_del(&str->link);
js_free_rt(rt, str);
}
if (rt->rt_info)
printf("\n");
}
{
JSMallocState *s = &rt->malloc_state;
if (s->malloc_count > 1) {
if (rt->rt_info)
printf("%s:1: ", rt->rt_info);
printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
(uint64_t)(s->malloc_size - sizeof(JSRuntime)),
(uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
}
}
#endif
{
JSMallocState ms = rt->malloc_state;
rt->mf.js_free(&ms, rt);
}
}
JSContext *JS_NewContextRaw(JSRuntime *rt)
{
JSContext *ctx;
int i;
ctx = js_mallocz_rt(rt, sizeof(JSContext));
if (!ctx)
return NULL;
ctx->header.ref_count = 1;
add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
rt->class_count);
if (!ctx->class_proto) {
js_free_rt(rt, ctx);
return NULL;
}
ctx->rt = rt;
list_add_tail(&ctx->link, &rt->context_list);
#ifdef CONFIG_BIGNUM
ctx->bf_ctx = &rt->bf_ctx;
ctx->fp_env.prec = 113;
ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
#endif
for(i = 0; i < rt->class_count; i++)
ctx->class_proto[i] = JS_NULL;
ctx->array_ctor = JS_NULL;
ctx->regexp_ctor = JS_NULL;
ctx->promise_ctor = JS_NULL;
init_list_head(&ctx->loaded_modules);
JS_AddIntrinsicBasicObjects(ctx);
return ctx;
}
JSContext *JS_NewContext(JSRuntime *rt)
{
JSContext *ctx;
ctx = JS_NewContextRaw(rt);
if (!ctx)
return NULL;
JS_AddIntrinsicBaseObjects(ctx);
JS_AddIntrinsicDate(ctx);
JS_AddIntrinsicEval(ctx);
JS_AddIntrinsicStringNormalize(ctx);
JS_AddIntrinsicRegExp(ctx);
JS_AddIntrinsicJSON(ctx);
JS_AddIntrinsicProxy(ctx);
JS_AddIntrinsicMapSet(ctx);
JS_AddIntrinsicTypedArrays(ctx);
JS_AddIntrinsicPromise(ctx);
#ifdef CONFIG_BIGNUM
JS_AddIntrinsicBigInt(ctx);
#endif
return ctx;
}
void *JS_GetContextOpaque(JSContext *ctx)
{
return ctx->user_opaque;
}
void JS_SetContextOpaque(JSContext *ctx, void *opaque)
{
ctx->user_opaque = opaque;
}
/* set the new value and free the old value after (freeing the value
can reallocate the object data) */
static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
{
JSValue old_val;
old_val = *pval;
*pval = new_val;
JS_FreeValue(ctx, old_val);
}
void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
{
JSRuntime *rt = ctx->rt;
assert(class_id < rt->class_count);
set_value(ctx, &ctx->class_proto[class_id], obj);
}
JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
{
JSRuntime *rt = ctx->rt;
assert(class_id < rt->class_count);
return JS_DupValue(ctx, ctx->class_proto[class_id]);
}
typedef enum JSFreeModuleEnum {
JS_FREE_MODULE_ALL,
JS_FREE_MODULE_NOT_RESOLVED,
JS_FREE_MODULE_NOT_EVALUATED,
} JSFreeModuleEnum;
/* XXX: would be more efficient with separate module lists */
static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
{
struct list_head *el, *el1;
list_for_each_safe(el, el1, &ctx->loaded_modules) {
JSModuleDef *m = list_entry(el, JSModuleDef, link);
if (flag == JS_FREE_MODULE_ALL ||
(flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||
(flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {
js_free_module_def(ctx, m);
}
}
}
JSContext *JS_DupContext(JSContext *ctx)
{
ctx->header.ref_count++;
return ctx;
}
/* used by the GC */
static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
JS_MarkFunc *mark_func)
{
int i;
struct list_head *el;
/* modules are not seen by the GC, so we directly mark the objects
referenced by each module */
list_for_each(el, &ctx->loaded_modules) {
JSModuleDef *m = list_entry(el, JSModuleDef, link);
js_mark_module_def(rt, m, mark_func);
}
JS_MarkValue(rt, ctx->global_obj, mark_func);
JS_MarkValue(rt, ctx->global_var_obj, mark_func);
JS_MarkValue(rt, ctx->throw_type_error, mark_func);
JS_MarkValue(rt, ctx->eval_obj, mark_func);
JS_MarkValue(rt, ctx->array_proto_values, mark_func);
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
}
for(i = 0; i < rt->class_count; i++) {
JS_MarkValue(rt, ctx->class_proto[i], mark_func);
}
JS_MarkValue(rt, ctx->iterator_proto, mark_func);
JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
JS_MarkValue(rt, ctx->promise_ctor, mark_func);
JS_MarkValue(rt, ctx->array_ctor, mark_func);
JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
JS_MarkValue(rt, ctx->function_ctor, mark_func);
JS_MarkValue(rt, ctx->function_proto, mark_func);
if (ctx->array_shape)
mark_func(rt, &ctx->array_shape->header);
}
void JS_FreeContext(JSContext *ctx)
{
JSRuntime *rt = ctx->rt;
int i;
if (--ctx->header.ref_count > 0)
return;
assert(ctx->header.ref_count == 0);
#ifdef DUMP_ATOMS
JS_DumpAtoms(ctx->rt);
#endif
#ifdef DUMP_SHAPES
JS_DumpShapes(ctx->rt);
#endif
#ifdef DUMP_OBJECTS
{
struct list_head *el;
JSGCObjectHeader *p;
printf("JSObjects: {\n");
JS_DumpObjectHeader(ctx->rt);
list_for_each(el, &rt->gc_obj_list) {
p = list_entry(el, JSGCObjectHeader, link);
JS_DumpGCObject(rt, p);
}
printf("}\n");
}
#endif
#ifdef DUMP_MEM
{
JSMemoryUsage stats;
JS_ComputeMemoryUsage(rt, &stats);
JS_DumpMemoryUsage(stdout, &stats, rt);
}
#endif
js_free_modules(ctx, JS_FREE_MODULE_ALL);
JS_FreeValue(ctx, ctx->global_obj);
JS_FreeValue(ctx, ctx->global_var_obj);
JS_FreeValue(ctx, ctx->throw_type_error);
JS_FreeValue(ctx, ctx->eval_obj);
JS_FreeValue(ctx, ctx->array_proto_values);
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
JS_FreeValue(ctx, ctx->native_error_proto[i]);
}
for(i = 0; i < rt->class_count; i++) {
JS_FreeValue(ctx, ctx->class_proto[i]);
}
js_free_rt(rt, ctx->class_proto);
JS_FreeValue(ctx, ctx->iterator_proto);
JS_FreeValue(ctx, ctx->async_iterator_proto);
JS_FreeValue(ctx, ctx->promise_ctor);
JS_FreeValue(ctx, ctx->array_ctor);
JS_FreeValue(ctx, ctx->regexp_ctor);
JS_FreeValue(ctx, ctx->function_ctor);
JS_FreeValue(ctx, ctx->function_proto);
js_free_shape_null(ctx->rt, ctx->array_shape);
list_del(&ctx->link);
remove_gc_object(&ctx->header);
js_free_rt(ctx->rt, ctx);
}
JSRuntime *JS_GetRuntime(JSContext *ctx)
{
return ctx->rt;
}
void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
{
rt->stack_size = stack_size;
}
static inline BOOL is_strict_mode(JSContext *ctx)
{
JSStackFrame *sf = ctx->rt->current_stack_frame;
return (sf && (sf->js_mode & JS_MODE_STRICT));
}
#ifdef CONFIG_BIGNUM
static inline BOOL is_math_mode(JSContext *ctx)
{
JSStackFrame *sf = ctx->rt->current_stack_frame;
return (sf && (sf->js_mode & JS_MODE_MATH));
}
#endif
/* JSAtom support */
#define JS_ATOM_TAG_INT (1U << 31)
#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
#define JS_ATOM_MAX ((1U << 30) - 1)
/* return the max count from the hash size */
#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
static inline BOOL __JS_AtomIsConst(JSAtom v)
{
#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
return (int32_t)v <= 0;
#else
return (int32_t)v < JS_ATOM_END;
#endif
}
static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
{
return (v & JS_ATOM_TAG_INT) != 0;
}
static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
{
return v | JS_ATOM_TAG_INT;
}
static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
{
return atom & ~JS_ATOM_TAG_INT;
}
static inline int is_num(int c)
{
return c >= '0' && c <= '9';
}
/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
{
uint32_t n;
uint64_t n64;
int c, i, len;
len = p->len;
if (len == 0 || len > 10)
return FALSE;
if (p->is_wide_char)
c = p->u.str16[0];
else
c = p->u.str8[0];
if (is_num(c)) {
if (c == '0') {
if (len != 1)
return FALSE;
n = 0;
} else {
n = c - '0';
for(i = 1; i < len; i++) {
if (p->is_wide_char)
c = p->u.str16[i];
else
c = p->u.str8[i];
if (!is_num(c))
return FALSE;
n64 = (uint64_t)n * 10 + (c - '0');
if ((n64 >> 32) != 0)
return FALSE;
n = n64;
}
}
*pval = n;
return TRUE;
} else {
return FALSE;
}
}
/* XXX: could use faster version ? */
static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
{
size_t i;
for(i = 0; i < len; i++)
h = h * 263 + str[i];
return h;
}
static inline uint32_t hash_string16(const uint16_t *str,
size_t len, uint32_t h)
{
size_t i;
for(i = 0; i < len; i++)
h = h * 263 + str[i];
return h;
}
static uint32_t hash_string(const JSString *str, uint32_t h)
{
if (str->is_wide_char)
h = hash_string16(str->u.str16, str->len, h);
else
h = hash_string8(str->u.str8, str->len, h);
return h;
}
static __maybe_unused void JS_DumpString(JSRuntime *rt,
const JSString *p)
{
int i, c, sep;
if (p == NULL) {
printf("<null>");
return;
}
printf("%d", p->header.ref_count);
sep = (p->header.ref_count == 1) ? '\"' : '\'';
putchar(sep);
for(i = 0; i < p->len; i++) {
if (p->is_wide_char)
c = p->u.str16[i];
else
c = p->u.str8[i];
if (c == sep || c == '\\') {
putchar('\\');
putchar(c);
} else if (c >= ' ' && c <= 126) {
putchar(c);
} else if (c == '\n') {
putchar('\\');
putchar('n');
} else {
printf("\\u%04x", c);
}
}
putchar(sep);
}
static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
{
JSAtomStruct *p;
int h, i;
/* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
printf("JSAtom count=%d size=%d hash_size=%d:\n",
rt->atom_count, rt->atom_size, rt->atom_hash_size);
printf("JSAtom hash table: {\n");
for(i = 0; i < rt->atom_hash_size; i++) {
h = rt->atom_hash[i];
if (h) {
printf(" %d:", i);
while (h) {
p = rt->atom_array[h];
printf(" ");
JS_DumpString(rt, p);
h = p->hash_next;
}
printf("\n");
}
}
printf("}\n");
printf("JSAtom table: {\n");
for(i = 0; i < rt->atom_size; i++) {
p = rt->atom_array[i];
if (!atom_is_free(p)) {
printf(" %d: { %d %08x ", i, p->atom_type, p->hash);
if (!(p->len == 0 && p->is_wide_char != 0))
JS_DumpString(rt, p);
printf(" %d }\n", p->hash_next);
}
}
printf("}\n");
}
static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
{
JSAtomStruct *p;
uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
new_hash_mask = new_hash_size - 1;
new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
if (!new_hash)
return -1;
for(i = 0; i < rt->atom_hash_size; i++) {
h = rt->atom_hash[i];
while (h != 0) {
p = rt->atom_array[h];
hash_next1 = p->hash_next;
/* add in new hash table */
j = p->hash & new_hash_mask;
p->hash_next = new_hash[j];
new_hash[j] = h;
h = hash_next1;
}
}
js_free_rt(rt, rt->atom_hash);
rt->atom_hash = new_hash;
rt->atom_hash_size = new_hash_size;
rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
// JS_DumpAtoms(rt);
return 0;
}
static int JS_InitAtoms(JSRuntime *rt)
{
int i, len, atom_type;
const char *p;
rt->atom_hash_size = 0;
rt->atom_hash = NULL;
rt->atom_count = 0;
rt->atom_size = 0;
rt->atom_free_index = 0;
if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */
return -1;
p = js_atom_init;
for(i = 1; i < JS_ATOM_END; i++) {
if (i == JS_ATOM_Private_brand)
atom_type = JS_ATOM_TYPE_PRIVATE;
else if (i >= JS_ATOM_Symbol_toPrimitive)
atom_type = JS_ATOM_TYPE_SYMBOL;
else
atom_type = JS_ATOM_TYPE_STRING;
len = strlen(p);
if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
return -1;
p = p + len + 1;
}
return 0;
}
static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
{
JSAtomStruct *p;
if (!__JS_AtomIsConst(v)) {
p = rt->atom_array[v];
p->header.ref_count++;
}
return v;
}
JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
{
JSRuntime *rt;
JSAtomStruct *p;
if (!__JS_AtomIsConst(v)) {
rt = ctx->rt;
p = rt->atom_array[v];
p->header.ref_count++;
}
return v;
}
static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
{
JSRuntime *rt;
JSAtomStruct *p;
rt = ctx->rt;
if (__JS_AtomIsTaggedInt(v))
return JS_ATOM_KIND_STRING;
p = rt->atom_array[v];
switch(p->atom_type) {
case JS_ATOM_TYPE_STRING:
return JS_ATOM_KIND_STRING;
case JS_ATOM_TYPE_GLOBAL_SYMBOL:
return JS_ATOM_KIND_SYMBOL;
case JS_ATOM_TYPE_SYMBOL:
switch(p->hash) {
case JS_ATOM_HASH_SYMBOL:
return JS_ATOM_KIND_SYMBOL;
case JS_ATOM_HASH_PRIVATE:
return JS_ATOM_KIND_PRIVATE;
default:
abort();
}
default:
abort();
}
}
static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
{
return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
}
static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
{
uint32_t i = p->hash_next; /* atom_index */
if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
JSAtomStruct *p1;
i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
p1 = rt->atom_array[i];
while (p1 != p) {
assert(i != 0);
i = p1->hash_next;
p1 = rt->atom_array[i];
}
}
return i;
}
/* string case (internal). Return JS_ATOM_NULL if error. 'str' is
freed. */
static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
{
uint32_t h, h1, i;
JSAtomStruct *p;
int len;
#if 0
printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");
#endif
if (atom_type < JS_ATOM_TYPE_SYMBOL) {
/* str is not NULL */
if (str->atom_type == atom_type) {
/* str is the atom, return its index */
i = js_get_atom_index(rt, str);
/* reduce string refcount and increase atom's unless constant */
if (__JS_AtomIsConst(i))
str->header.ref_count--;
return i;
}
/* try and locate an already registered atom */
len = str->len;
h = hash_string(str, atom_type);
h &= JS_ATOM_HASH_MASK;
h1 = h & (rt->atom_hash_size - 1);
i = rt->atom_hash[h1];
while (i != 0) {
p = rt->atom_array[i];
if (p->hash == h &&
p->atom_type == atom_type &&
p->len == len &&
js_string_memcmp(p, str, len) == 0) {
if (!__JS_AtomIsConst(i))
p->header.ref_count++;
goto done;
}
i = p->hash_next;
}
} else {
h1 = 0; /* avoid warning */
if (atom_type == JS_ATOM_TYPE_SYMBOL) {
h = JS_ATOM_HASH_SYMBOL;
} else {
h = JS_ATOM_HASH_PRIVATE;
atom_type = JS_ATOM_TYPE_SYMBOL;
}
}
if (rt->atom_free_index == 0) {
/* allow new atom entries */
uint32_t new_size, start;
JSAtomStruct **new_array;
/* alloc new with size progression 3/2:
4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
preallocating space for predefined atoms (at least 195).
*/
new_size = max_int(211, rt->atom_size * 3 / 2);
if (new_size > JS_ATOM_MAX)
goto fail;
/* XXX: should use realloc2 to use slack space */
new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
if (!new_array)
goto fail;
/* Note: the atom 0 is not used */
start = rt->atom_size;
if (start == 0) {
/* JS_ATOM_NULL entry */
p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
if (!p) {
js_free_rt(rt, new_array);
goto fail;
}
p->header.ref_count = 1; /* not refcounted */
p->atom_type = JS_ATOM_TYPE_SYMBOL;
#ifdef DUMP_LEAKS
list_add_tail(&p->link, &rt->string_list);
#endif
new_array[0] = p;
rt->atom_count++;
start = 1;
}
rt->atom_size = new_size;
rt->atom_array = new_array;
rt->atom_free_index = start;
for(i = start; i < new_size; i++) {
uint32_t next;
if (i == (new_size - 1))
next = 0;
else
next = i + 1;
rt->atom_array[i] = atom_set_free(next);
}
}
if (str) {
if (str->atom_type == 0) {
p = str;
p->atom_type = atom_type;
} else {
p = js_malloc_rt(rt, sizeof(JSString) +
(str->len << str->is_wide_char) +
1 - str->is_wide_char);
if (unlikely(!p))
goto fail;
p->header.ref_count = 1;
p->is_wide_char = str->is_wide_char;
p->len = str->len;
#ifdef DUMP_LEAKS
list_add_tail(&p->link, &rt->string_list);
#endif
memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
1 - str->is_wide_char);
js_free_string(rt, str);
}
} else {
p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
if (!p)
return JS_ATOM_NULL;
p->header.ref_count = 1;
p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
p->len = 0;
#ifdef DUMP_LEAKS
list_add_tail(&p->link, &rt->string_list);
#endif
}
/* use an already free entry */
i = rt->atom_free_index;
rt->atom_free_index = atom_get_free(rt->atom_array[i]);
rt->atom_array[i] = p;
p->hash = h;
p->hash_next = i; /* atom_index */
p->atom_type = atom_type;
rt->atom_count++;
if (atom_type != JS_ATOM_TYPE_SYMBOL) {
p->hash_next = rt->atom_hash[h1];
rt->atom_hash[h1] = i;
if (unlikely(rt->atom_count >= rt->atom_count_resize))
JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
}
// JS_DumpAtoms(rt);
return i;
fail:
i = JS_ATOM_NULL;
done:
if (str)
js_free_string(rt, str);
return i;
}
/* only works with zero terminated 8 bit strings */
static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
int atom_type)
{
JSString *p;
p = js_alloc_string_rt(rt, len, 0);
if (!p)
return JS_ATOM_NULL;
memcpy(p->u.str8, str, len);
p->u.str8[len] = '\0';
return __JS_NewAtom(rt, p, atom_type);
}
static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
int atom_type)
{
uint32_t h, h1, i;
JSAtomStruct *p;
h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
h &= JS_ATOM_HASH_MASK;
h1 = h & (rt->atom_hash_size - 1);
i = rt->atom_hash[h1];
while (i != 0) {
p = rt->atom_array[i];
if (p->hash == h &&
p->atom_type == JS_ATOM_TYPE_STRING &&
p->len == len &&
p->is_wide_char == 0 &&
memcmp(p->u.str8, str, len) == 0) {
if (!__JS_AtomIsConst(i))
p->header.ref_count++;
return i;
}
i = p->hash_next;
}
return JS_ATOM_NULL;
}
static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
{
#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
if (unlikely(i == JS_ATOM_NULL)) {
p->header.ref_count = INT32_MAX / 2;
return;
}
#endif
uint32_t i = p->hash_next; /* atom_index */
if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
JSAtomStruct *p0, *p1;
uint32_t h0;
h0 = p->hash & (rt->atom_hash_size - 1);
i = rt->atom_hash[h0];
p1 = rt->atom_array[i];
if (p1 == p) {
rt->atom_hash[h0] = p1->hash_next;
} else {
for(;;) {
assert(i != 0);
p0 = p1;
i = p1->hash_next;
p1 = rt->atom_array[i];
if (p1 == p) {
p0->hash_next = p1->hash_next;
break;
}
}
}
}
/* insert in free atom list */
rt->atom_array[i] = atom_set_free(rt->atom_free_index);
rt->atom_free_index = i;
/* free the string structure */
#ifdef DUMP_LEAKS
list_del(&p->link);
#endif
js_free_rt(rt, p);
rt->atom_count--;
assert(rt->atom_count >= 0);
}
static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
{
JSAtomStruct *p;
p = rt->atom_array[i];
if (--p->header.ref_count > 0)
return;
JS_FreeAtomStruct(rt, p);
}
/* Warning: 'p' is freed */
static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
{
JSRuntime *rt = ctx->rt;
uint32_t n;
if (is_num_string(&n, p)) {
if (n <= JS_ATOM_MAX_INT) {
js_free_string(rt, p);
return __JS_AtomFromUInt32(n);
}
}
/* XXX: should generate an exception */
return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
}
JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
{
JSValue val;
if (len == 0 || !is_digit(*str)) {
JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
if (atom)
return atom;
}
val = JS_NewStringLen(ctx, str, len);
if (JS_IsException(val))
return JS_ATOM_NULL;
return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
}
JSAtom JS_NewAtom(JSContext *ctx, const char *str)
{
return JS_NewAtomLen(ctx, str, strlen(str));
}
JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
{
if (n <= JS_ATOM_MAX_INT) {
return __JS_AtomFromUInt32(n);
} else {
char buf[11];
JSValue val;
snprintf(buf, sizeof(buf), "%u", n);
val = JS_NewString(ctx, buf);
if (JS_IsException(val))
return JS_ATOM_NULL;
return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
JS_ATOM_TYPE_STRING);
}
}
static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
{
if ((uint64_t)n <= JS_ATOM_MAX_INT) {
return __JS_AtomFromUInt32((uint32_t)n);
} else {
char buf[24];
JSValue val;
snprintf(buf, sizeof(buf), "%" PRId64 , n);
val = JS_NewString(ctx, buf);
if (JS_IsException(val))
return JS_ATOM_NULL;
return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
JS_ATOM_TYPE_STRING);
}
}
/* 'p' is freed */
static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
{
JSRuntime *rt = ctx->rt;
JSAtom atom;
atom = __JS_NewAtom(rt, p, atom_type);
if (atom == JS_ATOM_NULL)
return JS_ThrowOutOfMemory(ctx);
return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
}
/* descr must be a non-numeric string atom */
static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
int atom_type)
{
JSRuntime *rt = ctx->rt;
JSString *p;
assert(!__JS_AtomIsTaggedInt(descr));
assert(descr < rt->atom_size);
p = rt->atom_array[descr];
JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
return JS_NewSymbol(ctx, p, atom_type);
}
#define ATOM_GET_STR_BUF_SIZE 64
/* Should only be used for debug. */
static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
JSAtom atom)
{
if (__JS_AtomIsTaggedInt(atom)) {
snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
} else {
JSAtomStruct *p;
assert(atom < rt->atom_size);
if (atom == JS_ATOM_NULL) {
snprintf(buf, buf_size, "<null>");
} else {
int i, c;
char *q;
JSString *str;
q = buf;
p = rt->atom_array[atom];
assert(!atom_is_free(p));
str = p;
if (str) {
if (!str->is_wide_char) {
/* special case ASCII strings */
c = 0;
for(i = 0; i < str->len; i++) {
c |= str->u.str8[i];
}
if (c < 0x80)
return (const char *)str->u.str8;
}
for(i = 0; i < str->len; i++) {
if (str->is_wide_char)
c = str->u.str16[i];
else
c = str->u.str8[i];
if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
break;
if (c < 128) {
*q++ = c;
} else {
q += unicode_to_utf8((uint8_t *)q, c);
}
}
}
*q = '\0';
}
}
return buf;
}
static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
{
return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
}
static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
{
char buf[ATOM_GET_STR_BUF_SIZE];
if (__JS_AtomIsTaggedInt(atom)) {
snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));
return JS_NewString(ctx, buf);
} else {
JSRuntime *rt = ctx->rt;
JSAtomStruct *p;
assert(atom < rt->atom_size);
p = rt->atom_array[atom];
if (p->atom_type == JS_ATOM_TYPE_STRING) {
goto ret_string;
} else if (force_string) {
if (p->len == 0 && p->is_wide_char != 0) {
/* no description string */
p = rt->atom_array[JS_ATOM_empty_string];
}
ret_string:
return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
} else {
return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
}
}
}
JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
{
return __JS_AtomToValue(ctx, atom, FALSE);
}
JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
{
return __JS_AtomToValue(ctx, atom, TRUE);
}
/* return TRUE if the atom is an array index (i.e. 0 <= index <=
2^32-2 and return its value */
static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
{
if (__JS_AtomIsTaggedInt(atom)) {
*pval = __JS_AtomToUInt32(atom);
return TRUE;
} else {
JSRuntime *rt = ctx->rt;
JSAtomStruct *p;
uint32_t val;
assert(atom < rt->atom_size);
p = rt->atom_array[atom];
if (p->atom_type == JS_ATOM_TYPE_STRING &&
is_num_string(&val, p) && val != -1) {
*pval = val;
return TRUE;
} else {
*pval = 0;
return FALSE;
}
}
}
/* This test must be fast if atom is not a numeric index (e.g. a
method name). Return JS_UNDEFINED if not a numeric
index. JS_EXCEPTION can also be returned. */
static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
{
JSRuntime *rt = ctx->rt;
JSAtomStruct *p1;
JSString *p;
int c, len, ret;
JSValue num, str;
if (__JS_AtomIsTaggedInt(atom))
return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
assert(atom < rt->atom_size);
p1 = rt->atom_array[atom];
if (p1->atom_type != JS_ATOM_TYPE_STRING)
return JS_UNDEFINED;
p = p1;
len = p->len;
if (p->is_wide_char) {
const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;
if (r >= r_end)
return JS_UNDEFINED;
c = *r;
if (c == '-') {
if (r >= r_end)
return JS_UNDEFINED;
r++;
c = *r;
/* -0 case is specific */
if (c == '0' && len == 2)
goto minus_zero;
}
/* XXX: should test NaN, but the tests do not check it */
if (!is_num(c)) {
/* XXX: String should be normalized, therefore 8-bit only */
const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
if (!(c =='I' && (r_end - r) == 8 &&
!memcmp(r + 1, nfinity16, sizeof(nfinity16))))
return JS_UNDEFINED;
}
} else {
const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;
if (r >= r_end)
return JS_UNDEFINED;
c = *r;
if (c == '-') {
if (r >= r_end)
return JS_UNDEFINED;
r++;
c = *r;
/* -0 case is specific */
if (c == '0' && len == 2) {
minus_zero:
return __JS_NewFloat64(ctx, -0.0);
}
}
if (!is_num(c)) {
if (!(c =='I' && (r_end - r) == 8 &&
!memcmp(r + 1, "nfinity", 7)))
return JS_UNDEFINED;
}
}
/* XXX: bignum: would be better to only accept integer to avoid
relying on current floating point precision */
/* this is ECMA CanonicalNumericIndexString primitive */
num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
if (JS_IsException(num))
return num;
str = JS_ToString(ctx, num);
if (JS_IsException(str)) {
JS_FreeValue(ctx, num);
return str;
}
ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
JS_FreeValue(ctx, str);
if (ret == 0) {
return num;
} else {
JS_FreeValue(ctx, num);
return JS_UNDEFINED;
}
}
/* return -1 if exception or TRUE/FALSE */
static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
{
JSValue num;
num = JS_AtomIsNumericIndex1(ctx, atom);
if (likely(JS_IsUndefined(num)))
return FALSE;
if (JS_IsException(num))
return -1;
JS_FreeValue(ctx, num);
return TRUE;
}
void JS_FreeAtom(JSContext *ctx, JSAtom v)
{
if (!__JS_AtomIsConst(v))
__JS_FreeAtom(ctx->rt, v);
}
void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
{
if (!__JS_AtomIsConst(v))
__JS_FreeAtom(rt, v);
}
/* return TRUE if 'v' is a symbol with a string description */
static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
{
JSRuntime *rt;
JSAtomStruct *p;
rt = ctx->rt;
if (__JS_AtomIsTaggedInt(v))
return FALSE;
p = rt->atom_array[v];
return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
p->hash == JS_ATOM_HASH_SYMBOL) ||
p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
!(p->len == 0 && p->is_wide_char != 0));
}
static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
{
char buf[ATOM_GET_STR_BUF_SIZE];
const char *p;
int i;
/* XXX: should handle embedded null characters */
/* XXX: should move encoding code to JS_AtomGetStr */
p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
for (i = 0; p[i]; i++) {
int c = (unsigned char)p[i];
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
break;
}
if (i > 0 && p[i] == '\0') {
printf("%s", p);
} else {
putchar('"');
printf("%.*s", i, p);
for (; p[i]; i++) {
int c = (unsigned char)p[i];
if (c == '\"' || c == '\\') {
putchar('\\');
putchar(c);
} else if (c >= ' ' && c <= 126) {
putchar(c);
} else if (c == '\n') {
putchar('\\');
putchar('n');
} else {
printf("\\u%04x", c);
}
}
putchar('\"');
}
}
/* free with JS_FreeCString() */
const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
{
JSValue str;
const char *cstr;
str = JS_AtomToString(ctx, atom);
if (JS_IsException(str))
return NULL;
cstr = JS_ToCString(ctx, str);
JS_FreeValue(ctx, str);
return cstr;
}
/* return a string atom containing name concatenated with str1 */
static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
{
JSValue str;
JSAtom atom;
const char *cstr;
char *cstr2;
size_t len, len1;
str = JS_AtomToString(ctx, name);
if (JS_IsException(str))
return JS_ATOM_NULL;
cstr = JS_ToCStringLen(ctx, &len, str);
if (!cstr)
goto fail;
len1 = strlen(str1);
cstr2 = js_malloc(ctx, len + len1 + 1);
if (!cstr2)
goto fail;
memcpy(cstr2, cstr, len);
memcpy(cstr2 + len, str1, len1);
cstr2[len + len1] = '\0';
atom = JS_NewAtomLen(ctx, cstr2, len + len1);
js_free(ctx, cstr2);
JS_FreeCString(ctx, cstr);
JS_FreeValue(ctx, str);
return atom;
fail:
JS_FreeCString(ctx, cstr);
JS_FreeValue(ctx, str);
return JS_ATOM_NULL;
}
static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
{
char buf[16];
snprintf(buf, sizeof(buf), "%u", n);
return js_atom_concat_str(ctx, name, buf);
}
static inline BOOL JS_IsEmptyString(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
}
/* JSClass support */
/* a new class ID is allocated if *pclass_id != 0 */
JSClassID JS_NewClassID(JSClassID *pclass_id)
{
JSClassID class_id;
/* XXX: make it thread safe */
class_id = *pclass_id;
if (class_id == 0) {
class_id = js_class_id_alloc++;
*pclass_id = class_id;
}
return class_id;
}
BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
{
return (class_id < rt->class_count &&
rt->class_array[class_id].class_id != 0);
}
/* create a new object internal class. Return -1 if error, 0 if
OK. The finalizer can be NULL if none is needed. */
static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
const JSClassDef *class_def, JSAtom name)
{
int new_size, i;
JSClass *cl, *new_class_array;
struct list_head *el;
if (class_id < rt->class_count &&
rt->class_array[class_id].class_id != 0)
return -1;
if (class_id >= rt->class_count) {
new_size = max_int(JS_CLASS_INIT_COUNT,
max_int(class_id + 1, rt->class_count * 3 / 2));
/* reallocate the context class prototype array, if any */
list_for_each(el, &rt->context_list) {
JSContext *ctx = list_entry(el, JSContext, link);
JSValue *new_tab;
new_tab = js_realloc_rt(rt, ctx->class_proto,
sizeof(ctx->class_proto[0]) * new_size);
if (!new_tab)
return -1;
for(i = rt->class_count; i < new_size; i++)
new_tab[i] = JS_NULL;
ctx->class_proto = new_tab;
}
/* reallocate the class array */
new_class_array = js_realloc_rt(rt, rt->class_array,
sizeof(JSClass) * new_size);
if (!new_class_array)
return -1;
memset(new_class_array + rt->class_count, 0,
(new_size - rt->class_count) * sizeof(JSClass));
rt->class_array = new_class_array;
rt->class_count = new_size;
}
cl = &rt->class_array[class_id];
cl->class_id = class_id;
cl->class_name = JS_DupAtomRT(rt, name);
cl->finalizer = class_def->finalizer;
cl->gc_mark = class_def->gc_mark;
cl->call = class_def->call;
cl->exotic = class_def->exotic;
return 0;
}
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
{
int ret, len;
JSAtom name;
len = strlen(class_def->class_name);
name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
if (name == JS_ATOM_NULL) {
name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
if (name == JS_ATOM_NULL)
return -1;
}
ret = JS_NewClass1(rt, class_id, class_def, name);
JS_FreeAtomRT(rt, name);
return ret;
}
static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)
{
JSString *str;
if (len <= 0) {
return JS_AtomToString(ctx, JS_ATOM_empty_string);
}
str = js_alloc_string(ctx, len, 0);
if (!str)
return JS_EXCEPTION;
memcpy(str->u.str8, buf, len);
str->u.str8[len] = '\0';
return JS_MKPTR(JS_TAG_STRING, str);
}
static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)
{
JSString *str;
str = js_alloc_string(ctx, len, 1);
if (!str)
return JS_EXCEPTION;
memcpy(str->u.str16, buf, len * 2);
return JS_MKPTR(JS_TAG_STRING, str);
}
static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
{
if (c < 0x100) {
uint8_t ch8 = c;
return js_new_string8(ctx, &ch8, 1);
} else {
uint16_t ch16 = c;
return js_new_string16(ctx, &ch16, 1);
}
}
static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
{
int len = end - start;
if (start == 0 && end == p->len) {
return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
}
if (p->is_wide_char && len > 0) {
JSString *str;
int i;
uint16_t c = 0;
for (i = start; i < end; i++) {
c |= p->u.str16[i];
}
if (c > 0xFF)
return js_new_string16(ctx, p->u.str16 + start, len);
str = js_alloc_string(ctx, len, 0);
if (!str)
return JS_EXCEPTION;
for (i = 0; i < len; i++) {
str->u.str8[i] = p->u.str16[start + i];
}
str->u.str8[len] = '\0';
return JS_MKPTR(JS_TAG_STRING, str);
} else {
return js_new_string8(ctx, p->u.str8 + start, len);
}
}
typedef struct StringBuffer {
JSContext *ctx;
JSString *str;
int len;
int size;
int is_wide_char;
int error_status;
} StringBuffer;
/* It is valid to call string_buffer_end() and all string_buffer functions even
if string_buffer_init() or another string_buffer function returns an error.
If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
*/
static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
int is_wide)
{
s->ctx = ctx;
s->size = size;
s->len = 0;
s->is_wide_char = is_wide;
s->error_status = 0;
s->str = js_alloc_string(ctx, size, is_wide);
if (unlikely(!s->str)) {
s->size = 0;
return s->error_status = -1;
}
#ifdef DUMP_LEAKS
/* the StringBuffer may reallocate the JSString, only link it at the end */
list_del(&s->str->link);
#endif
return 0;
}
static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
{
return string_buffer_init2(ctx, s, size, 0);
}
static void string_buffer_free(StringBuffer *s)
{
js_free(s->ctx, s->str);
s->str = NULL;
}
static int string_buffer_set_error(StringBuffer *s)
{
js_free(s->ctx, s->str);
s->str = NULL;
s->size = 0;
s->len = 0;
return s->error_status = -1;
}
static no_inline int string_buffer_widen(StringBuffer *s, int size)
{
JSString *str;
size_t slack;
int i;
if (s->error_status)
return -1;
str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
if (!str)
return string_buffer_set_error(s);
size += slack >> 1;
for(i = s->len; i-- > 0;) {
str->u.str16[i] = str->u.str8[i];
}
s->is_wide_char = 1;
s->size = size;
s->str = str;
return 0;
}
static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
{
JSString *new_str;
int new_size;
size_t new_size_bytes, slack;
if (s->error_status)
return -1;
if (new_len > JS_STRING_LEN_MAX) {
JS_ThrowInternalError(s->ctx, "string too long");
return string_buffer_set_error(s);
}
new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
if (!s->is_wide_char && c >= 0x100) {
return string_buffer_widen(s, new_size);
}
new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
if (!new_str)
return string_buffer_set_error(s);
new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
s->size = new_size;
s->str = new_str;
return 0;
}
static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
{
if (unlikely(s->len >= s->size)) {
if (string_buffer_realloc(s, s->len + 1, c))
return -1;
}
if (s->is_wide_char) {
s->str->u.str16[s->len++] = c;
} else if (c < 0x100) {
s->str->u.str8[s->len++] = c;
} else {
if (string_buffer_widen(s, s->size))
return -1;
s->str->u.str16[s->len++] = c;
}
return 0;
}
/* 0 <= c <= 0xff */
static int string_buffer_putc8(StringBuffer *s, uint32_t c)
{
if (unlikely(s->len >= s->size)) {
if (string_buffer_realloc(s, s->len + 1, c))
return -1;
}
if (s->is_wide_char) {
s->str->u.str16[s->len++] = c;
} else {
s->str->u.str8[s->len++] = c;
}
return 0;
}
/* 0 <= c <= 0xffff */
static int string_buffer_putc16(StringBuffer *s, uint32_t c)
{
if (likely(s->len < s->size)) {
if (s->is_wide_char) {
s->str->u.str16[s->len++] = c;
return 0;
} else if (c < 0x100) {
s->str->u.str8[s->len++] = c;
return 0;
}
}
return string_buffer_putc_slow(s, c);
}
/* 0 <= c <= 0x10ffff */
static int string_buffer_putc(StringBuffer *s, uint32_t c)
{
if (unlikely(c >= 0x10000)) {
/* surrogate pair */
c -= 0x10000;
if (string_buffer_putc16(s, (c >> 10) + 0xd800))
return -1;
c = (c & 0x3ff) + 0xdc00;
}
return string_buffer_putc16(s, c);
}
static int string_get(const JSString *p, int idx) {
return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
}
static int string_getc(const JSString *p, int *pidx)
{
int idx, c, c1;
idx = *pidx;
if (p->is_wide_char) {
c = p->u.str16[idx++];
if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {
c1 = p->u.str16[idx];
if (c1 >= 0xdc00 && c1 < 0xe000) {
c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
idx++;
}
}
} else {
c = p->u.str8[idx++];
}
*pidx = idx;
return c;
}
static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
{
int i;
if (s->len + len > s->size) {
if (string_buffer_realloc(s, s->len + len, 0))
return -1;
}
if (s->is_wide_char) {
for (i = 0; i < len; i++) {
s->str->u.str16[s->len + i] = p[i];
}
s->len += len;
} else {
memcpy(&s->str->u.str8[s->len], p, len);
s->len += len;
}
return 0;
}
static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
{
int c = 0, i;
for (i = 0; i < len; i++) {
c |= p[i];
}
if (s->len + len > s->size) {
if (string_buffer_realloc(s, s->len + len, c))
return -1;
} else if (!s->is_wide_char && c >= 0x100) {
if (string_buffer_widen(s, s->size))
return -1;
}
if (s->is_wide_char) {
memcpy(&s->str->u.str16[s->len], p, len << 1);
s->len += len;
} else {
for (i = 0; i < len; i++) {
s->str->u.str8[s->len + i] = p[i];
}
s->len += len;
}
return 0;
}
/* appending an ASCII string */
static int string_buffer_puts8(StringBuffer *s, const char *str)
{
return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
}
static int string_buffer_concat(StringBuffer *s, const JSString *p,
uint32_t from, uint32_t to)
{
if (to <= from)
return 0;
if (p->is_wide_char)
return string_buffer_write16(s, p->u.str16 + from, to - from);
else
return string_buffer_write8(s, p->u.str8 + from, to - from);
}
static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
{
JSString *p;
JSValue v1;
int res;
if (s->error_status) {
/* prevent exception overload */
return -1;
}
if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
v1 = JS_ToString(s->ctx, v);
if (JS_IsException(v1))
return string_buffer_set_error(s);
p = JS_VALUE_GET_STRING(v1);
res = string_buffer_concat(s, p, 0, p->len);
JS_FreeValue(s->ctx, v1);
return res;
}
p = JS_VALUE_GET_STRING(v);
return string_buffer_concat(s, p, 0, p->len);
}
static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
{
JSString *p;
int res;
if (s->error_status) {
/* prevent exception overload */
JS_FreeValue(s->ctx, v);
return -1;
}
if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
v = JS_ToStringFree(s->ctx, v);
if (JS_IsException(v))
return string_buffer_set_error(s);
}
p = JS_VALUE_GET_STRING(v);
res = string_buffer_concat(s, p, 0, p->len);
JS_FreeValue(s->ctx, v);
return res;
}
static int string_buffer_fill(StringBuffer *s, int c, int count)
{
/* XXX: optimize */
if (s->len + count > s->size) {
if (string_buffer_realloc(s, s->len + count, c))
return -1;
}
while (count-- > 0) {
if (string_buffer_putc16(s, c))
return -1;
}
return 0;
}
static JSValue string_buffer_end(StringBuffer *s)
{
JSString *str;
str = s->str;
if (s->error_status)
return JS_EXCEPTION;
if (s->len == 0) {
js_free(s->ctx, str);
s->str = NULL;
return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
}
if (s->len < s->size) {
/* smaller size so js_realloc should not fail, but OK if it does */
/* XXX: should add some slack to avoid unnecessary calls */
/* XXX: might need to use malloc+free to ensure smaller size */
str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
(s->len << s->is_wide_char) + 1 - s->is_wide_char);
if (str == NULL)
str = s->str;
s->str = str;
}
if (!s->is_wide_char)
str->u.str8[s->len] = 0;
#ifdef DUMP_LEAKS
list_add_tail(&str->link, &s->ctx->rt->string_list);
#endif
str->is_wide_char = s->is_wide_char;
str->len = s->len;
s->str = NULL;
return JS_MKPTR(JS_TAG_STRING, str);
}
/* create a string from a UTF-8 buffer */
JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
{
const uint8_t *p, *p_end, *p_start, *p_next;
uint32_t c;
StringBuffer b_s, *b = &b_s;
size_t len1;
p_start = (const uint8_t *)buf;
p_end = p_start + buf_len;
p = p_start;
while (p < p_end && *p < 128)
p++;
len1 = p - p_start;
if (len1 > JS_STRING_LEN_MAX)
return JS_ThrowInternalError(ctx, "string too long");
if (p == p_end) {
/* ASCII string */
return js_new_string8(ctx, (const uint8_t *)buf, buf_len);
} else {
if (string_buffer_init(ctx, b, buf_len))
goto fail;
string_buffer_write8(b, p_start, len1);
while (p < p_end) {
if (*p < 128) {
string_buffer_putc8(b, *p++);
} else {
/* parse utf-8 sequence, return 0xFFFFFFFF for error */
c = unicode_from_utf8(p, p_end - p, &p_next);
if (c < 0x10000) {
p = p_next;
} else if (c <= 0x10FFFF) {
p = p_next;
/* surrogate pair */
c -= 0x10000;
string_buffer_putc16(b, (c >> 10) + 0xd800);
c = (c & 0x3ff) + 0xdc00;
} else {
/* invalid char */
c = 0xfffd;
/* skip the invalid chars */
/* XXX: seems incorrect. Why not just use c = *p++; ? */
while (p < p_end && (*p >= 0x80 && *p < 0xc0))
p++;
if (p < p_end) {
p++;
while (p < p_end && (*p >= 0x80 && *p < 0xc0))
p++;
}
}
string_buffer_putc16(b, c);
}
}
}
return string_buffer_end(b);
fail:
string_buffer_free(b);
return JS_EXCEPTION;
}
static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
JSValue str2, const char *str3)
{
StringBuffer b_s, *b = &b_s;
int len1, len3;
JSString *p;
if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
str2 = JS_ToStringFree(ctx, str2);
if (JS_IsException(str2))
goto fail;
}
p = JS_VALUE_GET_STRING(str2);
len1 = strlen(str1);
len3 = strlen(str3);
if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
goto fail;
string_buffer_write8(b, (const uint8_t *)str1, len1);
string_buffer_concat(b, p, 0, p->len);
string_buffer_write8(b, (const uint8_t *)str3, len3);
JS_FreeValue(ctx, str2);
return string_buffer_end(b);
fail:
JS_FreeValue(ctx, str2);
return JS_EXCEPTION;
}
JSValue JS_NewString(JSContext *ctx, const char *str)
{
return JS_NewStringLen(ctx, str, strlen(str));
}
JSValue JS_NewAtomString(JSContext *ctx, const char *str)
{
JSAtom atom = JS_NewAtom(ctx, str);
if (atom == JS_ATOM_NULL)
return JS_EXCEPTION;
JSValue val = JS_AtomToString(ctx, atom);
JS_FreeAtom(ctx, atom);
return val;
}
/* return (NULL, 0) if exception. */
/* return pointer into a JSString with a live ref_count */
/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */
const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
{
JSValue val;
JSString *str, *str_new;
int pos, len, c, c1;
uint8_t *q;
if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
val = JS_ToString(ctx, val1);
if (JS_IsException(val))
goto fail;
} else {
val = JS_DupValue(ctx, val1);
}
str = JS_VALUE_GET_STRING(val);
len = str->len;
if (!str->is_wide_char) {
const uint8_t *src = str->u.str8;
int count;
/* count the number of non-ASCII characters */
/* Scanning the whole string is required for ASCII strings,
and computing the number of non-ASCII bytes is less expensive
than testing each byte, hence this method is faster for ASCII
strings, which is the most common case.
*/
count = 0;
for (pos = 0; pos < len; pos++) {
count += src[pos] >> 7;
}
if (count == 0) {
if (plen)
*plen = len;
return (const char *)src;
}
str_new = js_alloc_string(ctx, len + count, 0);
if (!str_new)
goto fail;
q = str_new->u.str8;
for (pos = 0; pos < len; pos++) {
c = src[pos];
if (c < 0x80) {
*q++ = c;
} else {
*q++ = (c >> 6) | 0xc0;
*q++ = (c & 0x3f) | 0x80;