2020-09-06 18:53:08 +02:00
|
|
|
/*
|
|
|
|
|
* QuickJS Javascript Engine
|
2024-02-10 16:18:11 +01:00
|
|
|
*
|
2025-03-18 18:29:10 +01:00
|
|
|
* Copyright (c) 2017-2025 Fabrice Bellard
|
|
|
|
|
* Copyright (c) 2017-2025 Charlie Gordon
|
2020-09-06 18:53:08 +02:00
|
|
|
*
|
|
|
|
|
* 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>
|
2024-06-09 09:18:38 +02:00
|
|
|
#elif defined(__linux__) || defined(__GLIBC__)
|
2020-09-06 18:53:08 +02:00
|
|
|
#include <malloc.h>
|
2021-03-27 11:17:31 +01:00
|
|
|
#elif defined(__FreeBSD__)
|
|
|
|
|
#include <malloc_np.h>
|
2020-09-06 18:53:08 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "cutils.h"
|
|
|
|
|
#include "list.h"
|
|
|
|
|
#include "quickjs.h"
|
|
|
|
|
#include "libregexp.h"
|
2024-05-05 17:47:40 +02:00
|
|
|
#include "libunicode.h"
|
2025-03-19 19:07:57 +01:00
|
|
|
#include "dtoa.h"
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
2020-09-06 19:07:30 +02:00
|
|
|
#if !defined(EMSCRIPTEN)
|
|
|
|
|
/* enable stack limitation */
|
|
|
|
|
#define CONFIG_STACK_CHECK
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* 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
|
2023-12-13 19:02:47 +01:00
|
|
|
64: dump compute_stack_size
|
2020-09-06 18:53:08 +02:00
|
|
|
*/
|
|
|
|
|
//#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
|
2025-03-25 16:01:40 +01:00
|
|
|
//#define DUMP_ROPE_REBALANCE
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
/* 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) */
|
|
|
|
|
JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */
|
|
|
|
|
JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */
|
2025-05-05 19:12:32 +02:00
|
|
|
JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */
|
2020-09-06 18:53:08 +02:00
|
|
|
JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */
|
|
|
|
|
JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */
|
|
|
|
|
JS_CLASS_DATAVIEW, /* u.typed_array */
|
|
|
|
|
JS_CLASS_BIG_INT, /* u.object_data */
|
|
|
|
|
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 */
|
2025-04-05 12:49:29 +02:00
|
|
|
JS_CLASS_WEAK_REF,
|
|
|
|
|
JS_CLASS_FINALIZATION_REGISTRY,
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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,
|
2020-09-06 19:04:20 +02:00
|
|
|
JS_AGGREGATE_ERROR,
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
|
|
|
|
|
} JSErrorEnum;
|
|
|
|
|
|
2025-03-31 18:33:22 +02:00
|
|
|
/* the variable and scope indexes must fit on 16 bits. The (-1) and
|
|
|
|
|
ARG_SCOPE_END values are reserved. */
|
|
|
|
|
#define JS_MAX_LOCAL_VARS 65534
|
2020-11-08 14:30:56 +01:00
|
|
|
#define JS_STACK_SIZE_MAX 65534
|
2020-09-06 18:53:08 +02:00
|
|
|
#define JS_STRING_LEN_MAX ((1 << 30) - 1)
|
|
|
|
|
|
2025-03-25 16:01:40 +01:00
|
|
|
/* strings <= this length are not concatenated using ropes. if too
|
|
|
|
|
small, the rope memory overhead becomes high. */
|
|
|
|
|
#define JS_STRING_ROPE_SHORT_LEN 512
|
|
|
|
|
/* specific threshold for initial rope use */
|
|
|
|
|
#define JS_STRING_ROPE_SHORT2_LEN 8192
|
|
|
|
|
/* rope depth at which we rebalance */
|
|
|
|
|
#define JS_STRING_ROPE_MAX_DEPTH 60
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
#define __exception __attribute__((warn_unused_result))
|
|
|
|
|
|
|
|
|
|
typedef struct JSShape JSShape;
|
|
|
|
|
typedef struct JSString JSString;
|
|
|
|
|
typedef struct JSString JSAtomStruct;
|
2025-03-25 16:01:40 +01:00
|
|
|
typedef struct JSObject JSObject;
|
|
|
|
|
|
|
|
|
|
#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
|
|
|
|
|
#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
|
|
|
|
|
#define JS_VALUE_GET_STRING_ROPE(v) ((JSStringRope *)JS_VALUE_GET_PTR(v))
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
JS_GC_PHASE_NONE,
|
|
|
|
|
JS_GC_PHASE_DECREF,
|
|
|
|
|
JS_GC_PHASE_REMOVE_CYCLES,
|
|
|
|
|
} JSGCPhaseEnum;
|
|
|
|
|
|
|
|
|
|
typedef enum OPCodeEnum OPCodeEnum;
|
|
|
|
|
|
|
|
|
|
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() */
|
2024-02-10 16:18:11 +01:00
|
|
|
struct list_head gc_zero_ref_count_list;
|
2020-09-06 18:53:08 +02:00
|
|
|
struct list_head tmp_obj_list; /* used during GC */
|
|
|
|
|
JSGCPhaseEnum gc_phase : 8;
|
|
|
|
|
size_t malloc_gc_threshold;
|
2025-04-05 12:49:29 +02:00
|
|
|
struct list_head weakref_list; /* list of JSWeakRefHeader.link */
|
2020-09-06 18:53:08 +02:00
|
|
|
#ifdef DUMP_LEAKS
|
|
|
|
|
struct list_head string_list; /* list of JSString.link */
|
|
|
|
|
#endif
|
2020-09-06 19:04:20 +02:00
|
|
|
/* stack limitation */
|
2021-03-27 11:17:31 +01:00
|
|
|
uintptr_t stack_size; /* in bytes, 0 if no limit */
|
|
|
|
|
uintptr_t stack_top;
|
|
|
|
|
uintptr_t stack_limit; /* lower stack limit */
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 19:04:20 +02:00
|
|
|
JSValue current_exception;
|
2025-04-29 18:48:58 +02:00
|
|
|
/* true if the current exception cannot be catched */
|
|
|
|
|
BOOL current_exception_is_uncatchable : 8;
|
2020-09-06 19:04:20 +02:00
|
|
|
/* true if inside an out of memory error, to avoid recursing */
|
|
|
|
|
BOOL in_out_of_memory : 8;
|
|
|
|
|
|
|
|
|
|
struct JSStackFrame *current_stack_frame;
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
JSInterruptHandler *interrupt_handler;
|
|
|
|
|
void *interrupt_opaque;
|
|
|
|
|
|
|
|
|
|
JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
|
|
|
|
|
void *host_promise_rejection_tracker_opaque;
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
struct list_head job_list; /* list of JSJobEntry.link */
|
|
|
|
|
|
|
|
|
|
JSModuleNormalizeFunc *module_normalize_func;
|
2025-05-20 18:27:23 +02:00
|
|
|
BOOL module_loader_has_attr;
|
|
|
|
|
union {
|
|
|
|
|
JSModuleLoaderFunc *module_loader_func;
|
|
|
|
|
JSModuleLoaderFunc2 *module_loader_func2;
|
|
|
|
|
} u;
|
|
|
|
|
JSModuleCheckSupportedImportAttributes *module_check_attrs;
|
2020-09-06 18:53:08 +02:00
|
|
|
void *module_loader_opaque;
|
2023-12-27 17:10:47 +01:00
|
|
|
/* timestamp for internal use in module evaluation */
|
|
|
|
|
int64_t module_async_evaluation_next_timestamp;
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
BOOL can_block : 8; /* TRUE if Atomics.wait can block */
|
2020-09-06 19:07:30 +02:00
|
|
|
/* used to allocate, free and clone SharedArrayBuffers */
|
|
|
|
|
JSSharedArrayBufferFunctions sab_funcs;
|
2025-04-12 12:14:37 +02:00
|
|
|
/* see JS_SetStripInfo() */
|
|
|
|
|
uint8_t strip_flags;
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* Shape hash table */
|
|
|
|
|
int shape_hash_bits;
|
|
|
|
|
int shape_hash_size;
|
|
|
|
|
int shape_hash_count; /* number of hashed shapes */
|
|
|
|
|
JSShape **shape_hash;
|
2020-09-06 19:02:03 +02:00
|
|
|
void *user_opaque;
|
2020-09-06 18:53:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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)
|
2025-03-19 11:43:31 +01:00
|
|
|
#define JS_MODE_ASYNC (1 << 2) /* async function */
|
2025-03-20 13:37:07 +01:00
|
|
|
#define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
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 */
|
2023-12-27 17:21:46 +01:00
|
|
|
struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */
|
2020-09-06 18:53:08 +02:00
|
|
|
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
|
|
|
|
|
instruction after the call */
|
|
|
|
|
int arg_count;
|
2025-03-19 11:43:31 +01:00
|
|
|
int js_mode; /* not supported for C functions */
|
2020-09-06 18:53:08 +02:00
|
|
|
/* only used in generators. Current stack pointer value. NULL if
|
2024-02-10 16:18:11 +01:00
|
|
|
the function is running. */
|
2020-09-06 18:53:08 +02:00
|
|
|
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,
|
2020-09-06 19:04:20 +02:00
|
|
|
JS_GC_OBJ_TYPE_JS_CONTEXT,
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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;
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-05 12:49:29 +02:00
|
|
|
typedef enum {
|
|
|
|
|
JS_WEAKREF_TYPE_MAP,
|
|
|
|
|
JS_WEAKREF_TYPE_WEAKREF,
|
|
|
|
|
JS_WEAKREF_TYPE_FINREC,
|
|
|
|
|
} JSWeakRefHeaderTypeEnum;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
struct list_head link;
|
|
|
|
|
JSWeakRefHeaderTypeEnum weakref_type;
|
|
|
|
|
} JSWeakRefHeader;
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2025-03-13 17:44:55 +01:00
|
|
|
uint8_t is_detached;
|
2020-09-06 18:53:08 +02:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
JSValue *pvalue; /* pointer to the value, either on the stack or
|
|
|
|
|
to 'value' */
|
2023-12-27 17:21:46 +01:00
|
|
|
union {
|
|
|
|
|
JSValue value; /* used when is_detached = TRUE */
|
|
|
|
|
struct {
|
|
|
|
|
struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */
|
|
|
|
|
struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */
|
|
|
|
|
}; /* used when is_detached = FALSE */
|
|
|
|
|
};
|
2020-09-06 18:53:08 +02:00
|
|
|
} JSVarRef;
|
|
|
|
|
|
2025-03-18 18:29:10 +01:00
|
|
|
/* bigint */
|
2020-09-06 18:53:08 +02:00
|
|
|
|
2025-03-18 18:29:10 +01:00
|
|
|
#if JS_LIMB_BITS == 32
|
|
|
|
|
|
|
|
|
|
typedef int32_t js_slimb_t;
|
|
|
|
|
typedef uint32_t js_limb_t;
|
|
|
|
|
typedef int64_t js_sdlimb_t;
|
|
|
|
|
typedef uint64_t js_dlimb_t;
|
|
|
|
|
|
|
|
|
|
#define JS_LIMB_DIGITS 9
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
typedef __int128 int128_t;
|
|
|
|
|
typedef unsigned __int128 uint128_t;
|
|
|
|
|
typedef int64_t js_slimb_t;
|
|
|
|
|
typedef uint64_t js_limb_t;
|
|
|
|
|
typedef int128_t js_sdlimb_t;
|
|
|
|
|
typedef uint128_t js_dlimb_t;
|
|
|
|
|
|
|
|
|
|
#define JS_LIMB_DIGITS 19
|
2023-12-04 19:26:32 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
#endif
|
|
|
|
|
|
2025-03-18 18:29:10 +01:00
|
|
|
typedef struct JSBigInt {
|
|
|
|
|
JSRefCountHeader header; /* must come first, 32-bit */
|
|
|
|
|
uint32_t len; /* number of limbs, >= 1 */
|
|
|
|
|
js_limb_t tab[]; /* two's complement representation, always
|
|
|
|
|
normalized so that 'len' is the minimum
|
|
|
|
|
possible length >= 1 */
|
|
|
|
|
} JSBigInt;
|
|
|
|
|
|
|
|
|
|
/* this bigint structure can hold a 64 bit integer */
|
|
|
|
|
typedef struct {
|
2025-03-18 18:45:21 +01:00
|
|
|
js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */
|
2025-03-18 18:29:10 +01:00
|
|
|
/* must come just after */
|
|
|
|
|
js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS];
|
|
|
|
|
} JSBigIntBuf;
|
|
|
|
|
|
2020-09-06 19:04:20 +02:00
|
|
|
typedef enum {
|
|
|
|
|
JS_AUTOINIT_ID_PROTOTYPE,
|
|
|
|
|
JS_AUTOINIT_ID_MODULE_NS,
|
2020-09-06 19:07:30 +02:00
|
|
|
JS_AUTOINIT_ID_PROP,
|
2020-09-06 19:04:20 +02:00
|
|
|
} JSAutoInitIDEnum;
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* 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 {
|
2020-09-06 19:04:20 +02:00
|
|
|
JSGCObjectHeader header; /* must come first */
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2020-09-06 19:04:20 +02:00
|
|
|
JSValue array_ctor;
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2025-03-18 18:29:10 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* when the counter reaches zero, JSRutime.interrupt_handler is called */
|
|
|
|
|
int interrupt_counter;
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
JS_ATOM_KIND_STRING,
|
|
|
|
|
JS_ATOM_KIND_SYMBOL,
|
|
|
|
|
JS_ATOM_KIND_PRIVATE,
|
|
|
|
|
} JSAtomKindEnum;
|
|
|
|
|
|
|
|
|
|
#define JS_ATOM_HASH_MASK ((1 << 30) - 1)
|
2025-04-05 12:49:29 +02:00
|
|
|
#define JS_ATOM_HASH_PRIVATE JS_ATOM_HASH_MASK
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
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 */
|
2025-04-05 12:49:29 +02:00
|
|
|
/* for JS_ATOM_TYPE_SYMBOL: hash = weakref_count, atom_type = 3,
|
|
|
|
|
for JS_ATOM_TYPE_PRIVATE: hash = JS_ATOM_HASH_PRIVATE, atom_type = 3
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2025-03-25 16:01:40 +01:00
|
|
|
typedef struct JSStringRope {
|
|
|
|
|
JSRefCountHeader header; /* must come first, 32-bit */
|
|
|
|
|
uint32_t len;
|
|
|
|
|
uint8_t is_wide_char; /* 0 = 8 bits, 1 = 16 bits characters */
|
|
|
|
|
uint8_t depth; /* max depth of the rope tree */
|
|
|
|
|
/* XXX: could reduce memory usage by using a direct pointer with
|
|
|
|
|
bit 0 to select rope or string */
|
|
|
|
|
JSValue left;
|
|
|
|
|
JSValue right; /* might be the empty string */
|
|
|
|
|
} JSStringRope;
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
typedef struct JSClosureVar {
|
|
|
|
|
uint8_t is_local : 1;
|
|
|
|
|
uint8_t is_arg : 1;
|
|
|
|
|
uint8_t is_const : 1;
|
|
|
|
|
uint8_t is_lexical : 1;
|
2020-11-08 14:30:56 +01:00
|
|
|
uint8_t var_kind : 4; /* see JSVarKindEnum */
|
|
|
|
|
/* 8 bits available */
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
|
2020-11-08 14:30:56 +01:00
|
|
|
#define ARG_SCOPE_INDEX 1
|
|
|
|
|
#define ARG_SCOPE_END (-2)
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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,
|
2020-11-08 14:30:56 +01:00
|
|
|
JS_VAR_FUNCTION_NAME, /* function expression name */
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
|
2020-11-08 14:30:56 +01:00
|
|
|
/* XXX: could use a different structure in bytecode functions to save
|
|
|
|
|
memory */
|
2020-09-06 18:53:08 +02:00
|
|
|
typedef struct JSVarDef {
|
|
|
|
|
JSAtom var_name;
|
2020-11-08 14:30:56 +01:00
|
|
|
/* index into fd->scopes of this variable lexical scope */
|
|
|
|
|
int scope_level;
|
2024-02-10 16:18:11 +01:00
|
|
|
/* during compilation:
|
2020-11-08 14:30:56 +01:00
|
|
|
- if scope_level = 0: scope in which the variable is defined
|
|
|
|
|
- if scope_level != 0: index into fd->vars of the next
|
|
|
|
|
variable in the same or enclosing lexical scope
|
|
|
|
|
in a bytecode function:
|
|
|
|
|
index into fd->vars of the next
|
|
|
|
|
variable in the same or enclosing lexical scope
|
|
|
|
|
*/
|
2024-02-10 16:18:11 +01:00
|
|
|
int scope_next;
|
2020-09-06 18:53:08 +02:00
|
|
|
uint8_t is_const : 1;
|
|
|
|
|
uint8_t is_lexical : 1;
|
|
|
|
|
uint8_t is_captured : 1;
|
2023-12-09 12:32:32 +01:00
|
|
|
uint8_t is_static_private : 1; /* only used during private class field parsing */
|
2020-09-06 18:53:08 +02:00
|
|
|
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) */
|
2020-11-08 14:30:56 +01:00
|
|
|
int func_pool_idx : 24; /* only used during compilation : index in
|
|
|
|
|
the constant pool for hoisted function
|
|
|
|
|
definition */
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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 read_only_bytecode : 1;
|
2024-02-10 16:18:11 +01:00
|
|
|
uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */
|
2024-02-11 12:49:40 +01:00
|
|
|
/* XXX: 10 bits available */
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2020-09-06 19:04:20 +02:00
|
|
|
JSContext *realm; /* function realm */
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2025-04-14 14:46:47 +02:00
|
|
|
int source_len;
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
uint32_t idx;
|
2024-01-06 14:43:29 +01:00
|
|
|
uint32_t atom_count;
|
|
|
|
|
uint8_t in_prototype_chain;
|
|
|
|
|
uint8_t is_array;
|
|
|
|
|
JSPropertyEnum *tab_atom; /* is_array = FALSE */
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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 {
|
2023-12-27 17:21:46 +01:00
|
|
|
JSGCObjectHeader header;
|
|
|
|
|
JSValue this_val; /* 'this' argument */
|
2020-09-06 18:53:08 +02:00
|
|
|
int argc; /* number of function arguments */
|
|
|
|
|
BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
|
2023-12-27 17:21:46 +01:00
|
|
|
BOOL is_completed; /* TRUE if the function has returned. The stack
|
|
|
|
|
frame is no longer valid */
|
|
|
|
|
JSValue resolving_funcs[2]; /* only used in JS async functions */
|
2020-09-06 18:53:08 +02:00
|
|
|
JSStackFrame frame;
|
|
|
|
|
} JSAsyncFunctionState;
|
|
|
|
|
|
2020-09-06 18:57:11 +02:00
|
|
|
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;
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
typedef struct JSReqModuleEntry {
|
|
|
|
|
JSAtom module_name;
|
|
|
|
|
JSModuleDef *module; /* used using resolution */
|
2025-05-20 18:27:23 +02:00
|
|
|
JSValue attributes; /* JS_UNDEFINED or an object contains the attributes as key/value */
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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 */
|
2025-04-29 14:54:57 +02:00
|
|
|
BOOL is_star; /* import_name = '*' is a valid import name, so need a flag */
|
2020-09-06 18:53:08 +02:00
|
|
|
JSAtom import_name;
|
|
|
|
|
int req_module_idx; /* in req_module_entries */
|
|
|
|
|
} JSImportEntry;
|
|
|
|
|
|
2023-12-27 17:10:47 +01:00
|
|
|
typedef enum {
|
|
|
|
|
JS_MODULE_STATUS_UNLINKED,
|
|
|
|
|
JS_MODULE_STATUS_LINKING,
|
|
|
|
|
JS_MODULE_STATUS_LINKED,
|
|
|
|
|
JS_MODULE_STATUS_EVALUATING,
|
|
|
|
|
JS_MODULE_STATUS_EVALUATING_ASYNC,
|
|
|
|
|
JS_MODULE_STATUS_EVALUATED,
|
|
|
|
|
} JSModuleStatus;
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2023-12-27 17:10:47 +01:00
|
|
|
BOOL has_tla : 8; /* true if func_obj contains await */
|
2020-09-06 18:53:08 +02:00
|
|
|
BOOL resolved : 8;
|
2020-09-06 19:07:30 +02:00
|
|
|
BOOL func_created : 8;
|
2023-12-27 17:10:47 +01:00
|
|
|
JSModuleStatus status : 8;
|
|
|
|
|
/* temp use during js_module_link() & js_module_evaluate() */
|
|
|
|
|
int dfs_index, dfs_ancestor_index;
|
|
|
|
|
JSModuleDef *stack_prev;
|
|
|
|
|
/* temp use during js_module_evaluate() */
|
|
|
|
|
JSModuleDef **async_parent_modules;
|
|
|
|
|
int async_parent_modules_count;
|
|
|
|
|
int async_parent_modules_size;
|
|
|
|
|
int pending_async_dependencies;
|
|
|
|
|
BOOL async_evaluation;
|
|
|
|
|
int64_t async_evaluation_timestamp;
|
|
|
|
|
JSModuleDef *cycle_root;
|
|
|
|
|
JSValue promise; /* corresponds to spec field: capability */
|
|
|
|
|
JSValue resolving_funcs[2]; /* corresponds to spec field: capability */
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* true if evaluation yielded an exception. It is saved in
|
|
|
|
|
eval_exception */
|
2024-02-10 16:18:11 +01:00
|
|
|
BOOL eval_has_exception : 8;
|
2020-09-06 18:53:08 +02:00
|
|
|
JSValue eval_exception;
|
|
|
|
|
JSValue meta_obj; /* for import.meta */
|
2025-05-20 18:27:23 +02:00
|
|
|
JSValue private_value; /* private value for C modules */
|
2020-09-06 18:53:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 */
|
2020-09-06 19:04:20 +02:00
|
|
|
/* in order to use only 2 pointers, we compress the realm
|
|
|
|
|
and the init function pointer */
|
2020-09-06 19:07:30 +02:00
|
|
|
uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
|
|
|
|
|
in the 2 low bits */
|
2020-09-06 18:53:08 +02:00
|
|
|
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 {
|
2020-09-06 19:10:15 +02:00
|
|
|
/* hash table of size hash_mask + 1 before the start of the
|
|
|
|
|
structure (see prop_hash_end()). */
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2020-09-06 19:07:30 +02:00
|
|
|
int prop_count; /* include deleted properties */
|
|
|
|
|
int deleted_prop_count;
|
2020-09-06 18:53:08 +02:00
|
|
|
JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
|
|
|
|
|
JSObject *proto;
|
|
|
|
|
JSShapeProperty prop[0]; /* prop_size elements */
|
|
|
|
|
};
|
2020-09-06 19:07:30 +02:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2020-11-08 14:30:56 +01:00
|
|
|
uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */
|
2020-09-06 18:53:08 +02:00
|
|
|
uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
|
2025-04-29 19:03:36 +02:00
|
|
|
uint8_t has_immutable_prototype : 1; /* cannot modify the prototype */
|
2020-09-06 18:53:08 +02:00
|
|
|
uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
|
2020-09-06 19:10:15 +02:00
|
|
|
uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
|
2020-09-06 18:53:08 +02:00
|
|
|
uint16_t class_id; /* see JS_CLASS_x */
|
|
|
|
|
};
|
|
|
|
|
};
|
2025-04-05 12:49:29 +02:00
|
|
|
/* count the number of weak references to this object. The object
|
|
|
|
|
structure is freed only if header.ref_count = 0 and
|
|
|
|
|
weakref_count = 0 */
|
|
|
|
|
uint32_t weakref_count;
|
2020-09-06 18:53:08 +02:00
|
|
|
JSShape *shape; /* prototype and property names + flag */
|
|
|
|
|
JSProperty *prop; /* array of properties */
|
|
|
|
|
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 */
|
|
|
|
|
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 */
|
2023-12-27 17:21:46 +01:00
|
|
|
struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2020-09-06 19:04:20 +02:00
|
|
|
struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
|
|
|
|
|
JSContext *realm;
|
2020-09-06 18:53:08 +02:00
|
|
|
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 {
|
2024-02-10 16:18:11 +01:00
|
|
|
JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2025-05-05 19:12:32 +02:00
|
|
|
uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
};
|
2024-03-22 00:47:17 +01:00
|
|
|
|
2025-04-30 13:40:15 +02:00
|
|
|
typedef struct JSMapRecord {
|
|
|
|
|
int ref_count; /* used during enumeration to avoid freeing the record */
|
|
|
|
|
BOOL empty : 8; /* TRUE if the record is deleted */
|
|
|
|
|
struct list_head link;
|
|
|
|
|
struct JSMapRecord *hash_next;
|
|
|
|
|
JSValue key;
|
|
|
|
|
JSValue value;
|
|
|
|
|
} JSMapRecord;
|
|
|
|
|
|
|
|
|
|
typedef struct JSMapState {
|
|
|
|
|
BOOL is_weak; /* TRUE if WeakSet/WeakMap */
|
|
|
|
|
struct list_head records; /* list of JSMapRecord.link */
|
|
|
|
|
uint32_t record_count;
|
|
|
|
|
JSMapRecord **hash_table;
|
|
|
|
|
int hash_bits;
|
|
|
|
|
uint32_t hash_size; /* = 2 ^ hash_bits */
|
|
|
|
|
uint32_t record_count_threshold; /* count at which a hash table
|
|
|
|
|
resize is needed */
|
|
|
|
|
JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */
|
|
|
|
|
} JSMapState;
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
enum {
|
2020-09-06 19:10:15 +02:00
|
|
|
__JS_ATOM_NULL = JS_ATOM_NULL,
|
2020-09-06 18:53:08 +02:00
|
|
|
#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,
|
2020-11-08 14:30:56 +01:00
|
|
|
JSValue val, BOOL is_array_ctor);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2024-02-11 12:49:40 +01:00
|
|
|
static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2025-04-30 13:40:15 +02:00
|
|
|
static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val);
|
|
|
|
|
static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val);
|
2020-09-06 18:53:08 +02:00
|
|
|
static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
|
2025-05-05 15:59:04 +02:00
|
|
|
static void js_dump_value_write(void *opaque, const char *buf, size_t len);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2024-02-11 12:49:40 +01:00
|
|
|
static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
|
2020-09-06 18:53:08 +02:00
|
|
|
static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
|
2024-02-11 12:49:40 +01:00
|
|
|
static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
|
2020-09-06 19:04:20 +02:00
|
|
|
static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
|
2024-02-11 12:49:40 +01:00
|
|
|
static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2024-03-22 00:47:17 +01:00
|
|
|
|
|
|
|
|
#define HINT_STRING 0
|
|
|
|
|
#define HINT_NUMBER 1
|
|
|
|
|
#define HINT_NONE 2
|
|
|
|
|
#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive
|
|
|
|
|
static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2025-03-25 16:33:47 +01:00
|
|
|
static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2024-05-09 13:07:40 +02:00
|
|
|
static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2023-12-04 19:26:32 +01:00
|
|
|
static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
|
2020-09-06 18:53:08 +02:00
|
|
|
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
|
|
|
|
|
static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
|
2024-05-09 14:14:50 +02:00
|
|
|
|
|
|
|
|
static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception);
|
2020-09-06 18:53:08 +02:00
|
|
|
static int JS_CreateProperty(JSContext *ctx, JSObject *p,
|
|
|
|
|
JSAtom prop, JSValueConst val,
|
|
|
|
|
JSValueConst getter, JSValueConst setter,
|
|
|
|
|
int flags);
|
2025-03-25 16:01:40 +01:00
|
|
|
static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2,
|
|
|
|
|
int pos2, int len);
|
2020-09-06 19:07:30 +02:00
|
|
|
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);
|
2023-12-27 17:19:00 +01:00
|
|
|
static JSValue js_typed_array_constructor_ta(JSContext *ctx,
|
|
|
|
|
JSValueConst new_target,
|
|
|
|
|
JSValueConst src_obj,
|
|
|
|
|
int classid);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2023-12-27 17:21:46 +01:00
|
|
|
static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
|
|
|
|
|
static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2020-09-06 19:04:20 +02:00
|
|
|
static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
|
|
|
|
|
JS_MarkFunc *mark_func);
|
2020-09-06 18:53:08 +02:00
|
|
|
static JSValue js_import_meta(JSContext *ctx);
|
2025-05-20 18:27:23 +02:00
|
|
|
static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst options);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2023-12-27 17:10:47 +01:00
|
|
|
static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
int argc, JSValueConst *argv);
|
2020-09-06 18:53:08 +02:00
|
|
|
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 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);
|
2020-11-08 14:30:56 +01:00
|
|
|
static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
|
|
|
|
|
static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
|
2020-09-06 19:04:20 +02:00
|
|
|
void *opaque);
|
2020-11-08 14:30:56 +01:00
|
|
|
static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
|
|
|
|
|
JSAtom atom, void *opaque);
|
2023-12-27 17:18:39 +01:00
|
|
|
static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
int argc, JSValueConst *argv, int is_map);
|
2025-04-05 12:49:29 +02:00
|
|
|
static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh);
|
|
|
|
|
static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
|
|
|
|
|
static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
|
|
|
|
|
static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects);
|
2025-04-21 12:39:18 +02:00
|
|
|
static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
|
|
|
|
|
JSValueConst obj, JSValueConst method);
|
2025-04-29 14:54:57 +02:00
|
|
|
static int js_string_find_invalid_codepoint(JSString *p);
|
2025-04-30 13:40:15 +02:00
|
|
|
static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
int argc, JSValueConst *argv);
|
|
|
|
|
static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
int argc, JSValueConst *argv, int magic);
|
|
|
|
|
static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
int argc, JSValueConst *argv);
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:07:30 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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';
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 10:30:04 +01:00
|
|
|
static inline int string_get(const JSString *p, int idx) {
|
|
|
|
|
return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
2020-09-06 19:07:30 +02:00
|
|
|
{ JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
|
2020-09-06 18:53:08 +02:00
|
|
|
{ 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 */
|
2020-09-06 19:04:20 +02:00
|
|
|
{ JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
|
2020-09-06 18:53:08 +02:00
|
|
|
{ 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 */
|
|
|
|
|
{ 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 */
|
2025-05-05 19:12:32 +02:00
|
|
|
{ JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */
|
2020-09-06 18:53:08 +02:00
|
|
|
{ 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 */
|
|
|
|
|
{ JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
|
|
|
|
|
{ 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 */
|
2020-09-06 19:07:30 +02:00
|
|
|
{ JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
|
2020-09-06 18:53:08 +02:00
|
|
|
{ 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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:07:30 +02:00
|
|
|
#if !defined(CONFIG_STACK_CHECK)
|
|
|
|
|
/* no stack limitation */
|
2021-03-27 11:17:31 +01:00
|
|
|
static inline uintptr_t js_get_stack_pointer(void)
|
2020-09-06 19:04:20 +02:00
|
|
|
{
|
2021-03-27 11:17:31 +01:00
|
|
|
return 0;
|
2020-09-06 19:04:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
|
|
|
|
|
{
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
/* Note: OS and CPU dependent */
|
2021-03-27 11:17:31 +01:00
|
|
|
static inline uintptr_t js_get_stack_pointer(void)
|
2020-09-06 19:04:20 +02:00
|
|
|
{
|
2021-03-27 11:17:31 +01:00
|
|
|
return (uintptr_t)__builtin_frame_address(0);
|
2020-09-06 19:04:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
|
|
|
|
|
{
|
2021-03-27 11:17:31 +01:00
|
|
|
uintptr_t sp;
|
|
|
|
|
sp = js_get_stack_pointer() - alloca_size;
|
|
|
|
|
return unlikely(sp < rt->stack_limit);
|
2020-09-06 19:04:20 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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;
|
2025-04-05 12:49:29 +02:00
|
|
|
init_list_head(&rt->weakref_list);
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
#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;
|
2020-09-06 19:04:20 +02:00
|
|
|
|
|
|
|
|
rt->stack_size = JS_DEFAULT_STACK_SIZE;
|
2021-03-27 11:17:31 +01:00
|
|
|
JS_UpdateStackTop(rt);
|
|
|
|
|
|
2024-07-17 13:58:08 +02:00
|
|
|
rt->current_exception = JS_UNINITIALIZED;
|
2020-09-06 19:04:20 +02:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
return rt;
|
|
|
|
|
fail:
|
|
|
|
|
JS_FreeRuntime(rt);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:02:03 +02:00
|
|
|
void *JS_GetRuntimeOpaque(JSRuntime *rt)
|
|
|
|
|
{
|
|
|
|
|
return rt->user_opaque;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
|
|
|
|
|
{
|
|
|
|
|
rt->user_opaque = opaque;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* default memory allocation functions with memory limitation */
|
2024-01-11 15:29:19 +01:00
|
|
|
static size_t js_def_malloc_usable_size(const void *ptr)
|
2020-09-06 18:53:08 +02:00
|
|
|
{
|
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
|
return malloc_size(ptr);
|
|
|
|
|
#elif defined(_WIN32)
|
2024-01-11 15:29:19 +01:00
|
|
|
return _msize((void *)ptr);
|
2020-09-06 18:53:08 +02:00
|
|
|
#elif defined(EMSCRIPTEN)
|
|
|
|
|
return 0;
|
2024-06-09 09:18:38 +02:00
|
|
|
#elif defined(__linux__) || defined(__GLIBC__)
|
2024-01-11 15:29:19 +01:00
|
|
|
return malloc_usable_size((void *)ptr);
|
2020-09-06 18:53:08 +02:00
|
|
|
#else
|
|
|
|
|
/* change this to `return 0;` if compilation fails */
|
2024-01-11 15:29:19 +01:00
|
|
|
return malloc_usable_size((void *)ptr);
|
2020-09-06 18:53:08 +02:00
|
|
|
#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,
|
2024-01-11 15:29:19 +01:00
|
|
|
js_def_malloc_usable_size,
|
2020-09-06 18:53:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:07:30 +02:00
|
|
|
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
|
|
|
|
|
const JSSharedArrayBufferFunctions *sf)
|
|
|
|
|
{
|
|
|
|
|
rt->sab_funcs = *sf;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-12 12:14:37 +02:00
|
|
|
void JS_SetStripInfo(JSRuntime *rt, int flags)
|
|
|
|
|
{
|
|
|
|
|
rt->strip_flags = flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int JS_GetStripInfo(JSRuntime *rt)
|
|
|
|
|
{
|
|
|
|
|
return rt->strip_flags;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* 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;
|
|
|
|
|
|
2020-09-06 19:04:20 +02:00
|
|
|
JS_FreeValueRT(rt, rt->current_exception);
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2025-04-05 12:49:29 +02:00
|
|
|
/* don't remove the weak objects to avoid create new jobs with
|
|
|
|
|
FinalizationRegistry */
|
|
|
|
|
JS_RunGCInternal(rt, FALSE);
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
#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));
|
2025-04-05 12:49:29 +02:00
|
|
|
assert(list_empty(&rt->weakref_list));
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
/* 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 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:
|
2025-04-05 12:49:29 +02:00
|
|
|
if (p->hash != JS_ATOM_HASH_PRIVATE) {
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2020-09-06 19:04:20 +02:00
|
|
|
ctx->header.ref_count = 1;
|
|
|
|
|
add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
|
|
|
|
for(i = 0; i < rt->class_count; i++)
|
|
|
|
|
ctx->class_proto[i] = JS_NULL;
|
2020-09-06 19:04:20 +02:00
|
|
|
ctx->array_ctor = JS_NULL;
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2025-04-05 12:49:29 +02:00
|
|
|
JS_AddIntrinsicWeakRef(ctx);
|
2020-09-06 18:53:08 +02:00
|
|
|
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,
|
|
|
|
|
} 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 ||
|
2023-12-27 17:10:47 +01:00
|
|
|
(flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) {
|
2020-09-06 18:53:08 +02:00
|
|
|
js_free_module_def(ctx, m);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:04:20 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
void JS_FreeContext(JSContext *ctx)
|
|
|
|
|
{
|
|
|
|
|
JSRuntime *rt = ctx->rt;
|
|
|
|
|
int i;
|
|
|
|
|
|
2020-09-06 19:04:20 +02:00
|
|
|
if (--ctx->header.ref_count > 0)
|
|
|
|
|
return;
|
|
|
|
|
assert(ctx->header.ref_count == 0);
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
#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);
|
2020-09-06 19:04:20 +02:00
|
|
|
JS_FreeValue(ctx, ctx->array_ctor);
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
2020-09-06 19:04:20 +02:00
|
|
|
remove_gc_object(&ctx->header);
|
2020-09-06 18:53:08 +02:00
|
|
|
js_free_rt(ctx->rt, ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSRuntime *JS_GetRuntime(JSContext *ctx)
|
|
|
|
|
{
|
|
|
|
|
return ctx->rt;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-27 11:17:31 +01:00
|
|
|
static void update_stack_limit(JSRuntime *rt)
|
|
|
|
|
{
|
|
|
|
|
if (rt->stack_size == 0) {
|
|
|
|
|
rt->stack_limit = 0; /* no limit */
|
|
|
|
|
} else {
|
|
|
|
|
rt->stack_limit = rt->stack_top - rt->stack_size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:04:20 +02:00
|
|
|
void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
|
2020-09-06 18:53:08 +02:00
|
|
|
{
|
2020-09-06 19:04:20 +02:00
|
|
|
rt->stack_size = stack_size;
|
2021-03-27 11:17:31 +01:00
|
|
|
update_stack_limit(rt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void JS_UpdateStackTop(JSRuntime *rt)
|
|
|
|
|
{
|
|
|
|
|
rt->stack_top = js_get_stack_pointer();
|
|
|
|
|
update_stack_limit(rt);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline BOOL is_strict_mode(JSContext *ctx)
|
|
|
|
|
{
|
2020-09-06 19:04:20 +02:00
|
|
|
JSStackFrame *sf = ctx->rt->current_stack_frame;
|
2020-09-06 18:53:08 +02:00
|
|
|
return (sf && (sf->js_mode & JS_MODE_STRICT));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
2024-02-15 10:30:04 +01:00
|
|
|
c = string_get(p, 0);
|
2020-09-06 18:53:08 +02:00
|
|
|
if (is_num(c)) {
|
|
|
|
|
if (c == '0') {
|
|
|
|
|
if (len != 1)
|
|
|
|
|
return FALSE;
|
|
|
|
|
n = 0;
|
|
|
|
|
} else {
|
|
|
|
|
n = c - '0';
|
|
|
|
|
for(i = 1; i < len; i++) {
|
2024-02-15 10:30:04 +01:00
|
|
|
c = string_get(p, i);
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 16:01:40 +01:00
|
|
|
static uint32_t hash_string_rope(JSValueConst val, uint32_t h)
|
|
|
|
|
{
|
|
|
|
|
if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
|
|
|
|
|
return hash_string(JS_VALUE_GET_STRING(val), h);
|
|
|
|
|
} else {
|
|
|
|
|
JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
|
|
|
|
|
h = hash_string_rope(r->left, h);
|
|
|
|
|
return hash_string_rope(r->right, h);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-30 13:40:15 +02:00
|
|
|
static __maybe_unused void JS_DumpChar(FILE *fo, int c, int sep)
|
2024-02-15 10:30:04 +01:00
|
|
|
{
|
|
|
|
|
if (c == sep || c == '\\') {
|
2025-04-30 13:40:15 +02:00
|
|
|
fputc('\\', fo);
|
|
|
|
|
fputc(c, fo);
|
2024-02-15 10:30:04 +01:00
|
|
|
} else if (c >= ' ' && c <= 126) {
|
2025-04-30 13:40:15 +02:00
|
|
|
fputc(c, fo);
|
2024-02-15 10:30:04 +01:00
|
|
|
} else if (c == '\n') {
|
2025-04-30 13:40:15 +02:00
|
|
|
fputc('\\', fo);
|
|
|
|
|
fputc('n', fo);
|
2024-02-15 10:30:04 +01:00
|
|
|
} else {
|
2025-04-30 13:40:15 +02:00
|
|
|
fprintf(fo, "\\u%04x", c);
|
2024-02-15 10:30:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p)
|
2020-09-06 18:53:08 +02:00
|
|
|
{
|
2024-02-15 10:30:04 +01:00
|
|
|
int i, sep;
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
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++) {
|
2025-04-30 13:40:15 +02:00
|
|
|
JS_DumpChar(stdout, string_get(p, i), sep);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
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:
|
2025-04-05 12:49:29 +02:00
|
|
|
if (p->hash == JS_ATOM_HASH_PRIVATE)
|
2020-09-06 18:53:08 +02:00
|
|
|
return JS_ATOM_KIND_PRIVATE;
|
2025-04-05 12:49:29 +02:00
|
|
|
else
|
|
|
|
|
return JS_ATOM_KIND_SYMBOL;
|
2020-09-06 18:53:08 +02:00
|
|
|
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 &&
|
2025-03-25 16:01:40 +01:00
|
|
|
js_string_memcmp(p, 0, str, 0, len) == 0) {
|
2020-09-06 18:53:08 +02:00
|
|
|
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) {
|
2025-04-05 12:49:29 +02:00
|
|
|
h = 0;
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 10:30:04 +01:00
|
|
|
/* Warning: str must be ASCII only */
|
2020-09-06 18:53:08 +02:00
|
|
|
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
|
2025-04-05 12:49:29 +02:00
|
|
|
if (p->atom_type == JS_ATOM_TYPE_SYMBOL &&
|
|
|
|
|
p->hash != JS_ATOM_HASH_PRIVATE && p->hash != 0) {
|
|
|
|
|
/* live weak references are still present on this object: keep
|
|
|
|
|
it */
|
|
|
|
|
} else {
|
|
|
|
|
js_free_rt(rt, p);
|
|
|
|
|
}
|
2020-09-06 18:53:08 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 10:34:40 +02:00
|
|
|
/* XXX: optimize */
|
|
|
|
|
static size_t count_ascii(const uint8_t *buf, size_t len)
|
|
|
|
|
{
|
|
|
|
|
const uint8_t *p, *p_end;
|
|
|
|
|
p = buf;
|
|
|
|
|
p_end = buf + len;
|
|
|
|
|
while (p < p_end && *p < 128)
|
|
|
|
|
p++;
|
|
|
|
|
return p - buf;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 10:30:04 +01:00
|
|
|
/* str is UTF-8 encoded */
|
2020-09-06 18:53:08 +02:00
|
|
|
JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
|
|
|
|
|
{
|
|
|
|
|
JSValue val;
|
|
|
|
|
|
2025-04-10 10:34:40 +02:00
|
|
|
if (len == 0 ||
|
|
|
|
|
(!is_digit(*str) &&
|
|
|
|
|
count_ascii((const uint8_t *)str, len) == len)) {
|
2025-04-10 17:38:44 +02:00
|
|
|
JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2025-03-25 16:33:47 +01:00
|
|
|
size_t len;
|
|
|
|
|
len = u32toa(buf, n);
|
|
|
|
|
val = js_new_string8_len(ctx, buf, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2025-03-25 16:33:47 +01:00
|
|
|
size_t len;
|
|
|
|
|
len = i64toa(buf, n);
|
|
|
|
|
val = js_new_string8_len(ctx, buf, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
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++) {
|
2024-02-15 10:30:04 +01:00
|
|
|
c = string_get(str, i);
|
2020-09-06 18:53:08 +02:00
|
|
|
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)) {
|
2025-03-25 16:33:47 +01:00
|
|
|
size_t len = u32toa(buf, __JS_AtomToUInt32(atom));
|
|
|
|
|
return js_new_string8_len(ctx, buf, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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;
|
2025-04-16 15:04:57 +02:00
|
|
|
int c, ret;
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2025-04-16 15:04:57 +02:00
|
|
|
switch(atom) {
|
|
|
|
|
case JS_ATOM_minus_zero:
|
|
|
|
|
return __JS_NewFloat64(ctx, -0.0);
|
|
|
|
|
case JS_ATOM_Infinity:
|
|
|
|
|
return __JS_NewFloat64(ctx, INFINITY);
|
|
|
|
|
case JS_ATOM_minus_Infinity:
|
|
|
|
|
return __JS_NewFloat64(ctx, -INFINITY);
|
|
|
|
|
case JS_ATOM_NaN:
|
|
|
|
|
return __JS_NewFloat64(ctx, NAN);
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
2025-04-16 15:04:57 +02:00
|
|
|
p = p1;
|
|
|
|
|
if (p->len == 0)
|
|
|
|
|
return JS_UNDEFINED;
|
|
|
|
|
c = string_get(p, 0);
|
|
|
|
|
if (!is_num(c) && c != '-')
|
|
|
|
|
return JS_UNDEFINED;
|
2020-09-06 18:53:08 +02:00
|
|
|
/* 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 &&
|
2025-04-05 12:49:29 +02:00
|
|
|
p->hash != JS_ATOM_HASH_PRIVATE) ||
|
2020-09-06 18:53:08 +02:00
|
|
|
p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
|
|
|
|
|
!(p->len == 0 && p->is_wide_char != 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* free with JS_FreeCString() */
|
2025-05-20 18:03:29 +02:00
|
|
|
const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom)
|
2020-09-06 18:53:08 +02:00
|
|
|
{
|
|
|
|
|
JSValue str;
|
|
|
|
|
const char *cstr;
|
|
|
|
|
|
|
|
|
|
str = JS_AtomToString(ctx, atom);
|
2025-05-20 18:03:29 +02:00
|
|
|
if (JS_IsException(str)) {
|
|
|
|
|
if (plen)
|
|
|
|
|
*plen = 0;
|
2020-09-06 18:53:08 +02:00
|
|
|
return NULL;
|
2025-05-20 18:03:29 +02:00
|
|
|
}
|
|
|
|
|
cstr = JS_ToCStringLen(ctx, plen, str);
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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];
|
2025-03-25 16:33:47 +01:00
|
|
|
size_t len;
|
|
|
|
|
len = u32toa(buf, n);
|
|
|
|
|
buf[len] = '\0';
|
2020-09-06 18:53:08 +02:00
|
|
|
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 */
|
|
|
|
|
|
2024-01-02 16:08:48 +01:00
|
|
|
#ifdef CONFIG_ATOMICS
|
|
|
|
|
static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* a new class ID is allocated if *pclass_id != 0 */
|
|
|
|
|
JSClassID JS_NewClassID(JSClassID *pclass_id)
|
|
|
|
|
{
|
|
|
|
|
JSClassID class_id;
|
2024-01-02 16:08:48 +01:00
|
|
|
#ifdef CONFIG_ATOMICS
|
|
|
|
|
pthread_mutex_lock(&js_class_id_mutex);
|
|
|
|
|
#endif
|
2020-09-06 18:53:08 +02:00
|
|
|
class_id = *pclass_id;
|
|
|
|
|
if (class_id == 0) {
|
|
|
|
|
class_id = js_class_id_alloc++;
|
|
|
|
|
*pclass_id = class_id;
|
|
|
|
|
}
|
2024-01-02 16:08:48 +01:00
|
|
|
#ifdef CONFIG_ATOMICS
|
|
|
|
|
pthread_mutex_unlock(&js_class_id_mutex);
|
|
|
|
|
#endif
|
2020-09-06 18:53:08 +02:00
|
|
|
return class_id;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-20 09:29:08 +01:00
|
|
|
JSClassID JS_GetClassID(JSValue v)
|
|
|
|
|
{
|
|
|
|
|
JSObject *p;
|
|
|
|
|
if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
|
|
|
|
|
return JS_INVALID_CLASS_ID;
|
|
|
|
|
p = JS_VALUE_GET_OBJ(v);
|
|
|
|
|
return p->class_id;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
|
2021-03-27 11:17:31 +01:00
|
|
|
if (class_id >= (1 << 16))
|
|
|
|
|
return -1;
|
2020-09-06 18:53:08 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 16:33:47 +01:00
|
|
|
static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len)
|
2020-09-06 18:53:08 +02:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 16:33:47 +01:00
|
|
|
static JSValue js_new_string8(JSContext *ctx, const char *buf)
|
|
|
|
|
{
|
|
|
|
|
return js_new_string8_len(ctx, buf, strlen(buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len)
|
2020-09-06 18:53:08 +02:00
|
|
|
{
|
|
|
|
|
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;
|
2025-03-25 16:33:47 +01:00
|
|
|
return js_new_string8_len(ctx, (const char *)&ch8, 1);
|
2020-09-06 18:53:08 +02:00
|
|
|
} else {
|
|
|
|
|
uint16_t ch16 = c;
|
2025-03-25 16:33:47 +01:00
|
|
|
return js_new_string16_len(ctx, &ch16, 1);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2025-03-25 16:33:47 +01:00
|
|
|
return js_new_string16_len(ctx, p->u.str16 + start, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
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 {
|
2025-03-25 16:33:47 +01:00
|
|
|
return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 */
|
2024-02-20 00:22:32 +01:00
|
|
|
if (string_buffer_putc16(s, get_hi_surrogate(c)))
|
2020-09-06 18:53:08 +02:00
|
|
|
return -1;
|
2024-02-20 00:22:32 +01:00
|
|
|
c = get_lo_surrogate(c);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
return string_buffer_putc16(s, c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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++];
|
2024-02-20 00:22:32 +01:00
|
|
|
if (is_hi_surrogate(c) && idx < p->len) {
|
2020-09-06 18:53:08 +02:00
|
|
|
c1 = p->u.str16[idx];
|
2024-02-20 00:22:32 +01:00
|
|
|
if (is_lo_surrogate(c1)) {
|
|
|
|
|
c = from_surrogate(c, c1);
|
2020-09-06 18:53:08 +02:00
|
|
|
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)) {
|
2025-03-25 16:01:40 +01:00
|
|
|
if (JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE) {
|
|
|
|
|
JSStringRope *r = JS_VALUE_GET_STRING_ROPE(v);
|
|
|
|
|
/* recursion is acceptable because the rope depth is bounded */
|
|
|
|
|
if (string_buffer_concat_value(s, r->left))
|
|
|
|
|
return -1;
|
|
|
|
|
return string_buffer_concat_value(s, r->right);
|
|
|
|
|
} else {
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
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;
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
p_start = (const uint8_t *)buf;
|
|
|
|
|
p_end = p_start + buf_len;
|
2025-04-10 10:34:40 +02:00
|
|
|
len1 = count_ascii(p_start, buf_len);
|
|
|
|
|
p = p_start + len1;
|
2020-09-06 18:53:08 +02:00
|
|
|
if (len1 > JS_STRING_LEN_MAX)
|
|
|
|
|
return JS_ThrowInternalError(ctx, "string too long");
|
|
|
|
|
if (p == p_end) {
|
|
|
|
|
/* ASCII string */
|
2025-03-25 16:33:47 +01:00
|
|
|
return js_new_string8_len(ctx, buf, buf_len);
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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 */
|
2024-02-20 00:22:32 +01:00
|
|
|
string_buffer_putc16(b, get_hi_surrogate(c));
|
|
|
|
|
c = get_lo_surrogate(c);
|
2020-09-06 18:53:08 +02:00
|
|
|
} 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_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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const uint16_t *src = str->u.str16;
|
|
|
|
|
/* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
|
|
|
|
|
produce 4 bytes but use 2 code points.
|
|
|
|
|
*/
|
|
|
|
|
str_new = js_alloc_string(ctx, len * 3, 0);
|
|
|
|
|
if (!str_new)
|
|
|
|
|
goto fail;
|
|
|
|
|
q = str_new->u.str8;
|
|
|
|
|
pos = 0;
|
|
|
|
|
while (pos < len) {
|
|
|
|
|
c = src[pos++];
|
|
|
|
|
if (c < 0x80) {
|
|
|
|
|
*q++ = c;
|
|
|
|
|
} else {
|
2024-02-20 00:22:32 +01:00
|
|
|
if (is_hi_surrogate(c)) {
|
2020-09-06 18:53:08 +02:00
|
|
|
if (pos < len && !cesu8) {
|
|
|
|
|
c1 = src[pos];
|
2024-02-20 00:22:32 +01:00
|
|
|
if (is_lo_surrogate(c1)) {
|
2020-09-06 18:53:08 +02:00
|
|
|
pos++;
|
2024-02-20 00:22:32 +01:00
|
|
|
c = from_surrogate(c, c1);
|
2020-09-06 18:53:08 +02:00
|
|
|
} else {
|
|
|
|
|
/* Keep unmatched surrogate code points */
|
|
|
|
|
/* c = 0xfffd; */ /* error */
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* Keep unmatched surrogate code points */
|
|
|
|
|
/* c = 0xfffd; */ /* error */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
q += unicode_to_utf8(q, c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*q = '\0';
|
|
|
|
|
str_new->len = q - str_new->u.str8;
|
|
|
|
|
JS_FreeValue(ctx, val);
|
|
|
|
|
if (plen)
|
|
|
|
|
*plen = str_new->len;
|
|
|
|
|
return (const char *)str_new->u.str8;
|
|
|
|
|
fail:
|
|
|
|
|
if (plen)
|
|
|
|
|
*plen = 0;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void JS_FreeCString(JSContext *ctx, const char *ptr)
|
|
|
|
|
{
|
|
|
|
|
JSString *p;
|
|
|
|
|
if (!ptr)
|
|
|
|
|
return;
|
|
|
|
|
/* purposely removing constness */
|
2023-12-22 11:03:44 +01:00
|
|
|
p = container_of(ptr, JSString, u);
|
2020-09-06 18:53:08 +02:00
|
|
|
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
|
|
|
|
|
{
|
|
|
|
|
int c, i;
|
|
|
|
|
for(i = 0; i < len; i++) {
|
|
|
|
|
c = src1[i] - src2[i];
|
|
|
|
|
if (c != 0)
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
|
|
|
|
|
{
|
|
|
|
|
int c, i;
|
|
|
|
|
for(i = 0; i < len; i++) {
|
|
|
|
|
c = src1[i] - src2[i];
|
|
|
|
|
if (c != 0)
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 16:01:40 +01:00
|
|
|
static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2,
|
|
|
|
|
int pos2, int len)
|
2020-09-06 18:53:08 +02:00
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
if (likely(!p1->is_wide_char)) {
|
|
|
|
|
if (likely(!p2->is_wide_char))
|
2025-03-25 16:01:40 +01:00
|
|
|
res = memcmp(p1->u.str8 + pos1, p2->u.str8 + pos2, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
else
|
2025-03-25 16:01:40 +01:00
|
|
|
res = -memcmp16_8(p2->u.str16 + pos2, p1->u.str8 + pos1, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
} else {
|
|
|
|
|
if (!p2->is_wide_char)
|
2025-03-25 16:01:40 +01:00
|
|
|
res = memcmp16_8(p1->u.str16 + pos1, p2->u.str8 + pos2, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
else
|
2025-03-25 16:01:40 +01:00
|
|
|
res = memcmp16(p1->u.str16 + pos1, p2->u.str16 + pos2, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* return < 0, 0 or > 0 */
|
|
|
|
|
static int js_string_compare(JSContext *ctx,
|
|
|
|
|
const JSString *p1, const JSString *p2)
|
|
|
|
|
{
|
|
|
|
|
int res, len;
|
|
|
|
|
len = min_int(p1->len, p2->len);
|
2025-03-25 16:01:40 +01:00
|
|
|
res = js_string_memcmp(p1, 0, p2, 0, len);
|
2020-09-06 18:53:08 +02:00
|
|
|
if (res == 0) {
|
|
|
|
|
if (p1->len == p2->len)
|
|
|
|
|
res = 0;
|
|
|
|
|
else if (p1->len < p2->len)
|
|
|
|
|
res = -1;
|
|
|
|
|
else
|
|
|
|
|
res = 1;
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
|
|
|
|
|
{
|
|
|
|
|
if (p->is_wide_char) {
|
|
|
|
|
memcpy(dst, p->u.str16 + offset, len * 2);
|
|
|
|
|
} else {
|
|
|
|
|
const uint8_t *src1 = p->u.str8 + offset;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < len; i++)
|
|
|
|
|
dst[i] = src1[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSValue JS_ConcatString1(JSContext *ctx,
|
|
|
|
|
const JSString *p1, const JSString *p2)
|
|
|
|
|
{
|
|
|
|
|
JSString *p;
|
|
|
|
|
uint32_t len;
|
|
|
|
|
int is_wide_char;
|
|
|
|
|
|
|
|
|
|
len = p1->len + p2->len;
|
|
|
|
|
if (len > JS_STRING_LEN_MAX)
|
|
|
|
|
return JS_ThrowInternalError(ctx, "string too long");
|
|
|
|
|
is_wide_char = p1->is_wide_char | p2->is_wide_char;
|
|
|
|
|
p = js_alloc_string(ctx, len, is_wide_char);
|
|
|
|
|
if (!p)
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
if (!is_wide_char) {
|
|
|
|
|
memcpy(p->u.str8, p1->u.str8, p1->len);
|
|
|
|
|
memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
|
|
|
|
|
p->u.str8[len] = '\0';
|
|
|
|
|
} else {
|
|
|
|
|
copy_str16(p->u.str16, p1, 0, p1->len);
|
|
|
|
|
copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
|
|
|
|
|
}
|
|
|
|
|
return JS_MKPTR(JS_TAG_STRING, p);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-15 10:30:04 +01:00
|
|
|
static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) {
|
|
|
|
|
if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
|
|
|
|
|
JSString *p2 = JS_VALUE_GET_STRING(op2);
|
|
|
|
|
size_t size1;
|
|
|
|
|
|
|
|
|
|
if (p2->len == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
if (p1->header.ref_count != 1)
|
|
|
|
|
return FALSE;
|
|
|
|
|
size1 = js_malloc_usable_size(ctx, p1);
|
|
|
|
|
if (p1->is_wide_char) {
|
|
|
|
|
if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) {
|
|
|
|
|
if (p2->is_wide_char) {
|
|
|
|
|
memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
|
|
|
|
|
p1->len += p2->len;
|
|
|
|
|
return TRUE;
|
|
|
|
|
} else {
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < p2->len; i++) {
|
|
|
|
|
p1->u.str16[p1->len++] = p2->u.str8[i];
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (!p2->is_wide_char) {
|
|
|
|
|
if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) {
|
|
|
|
|
memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
|
|
|
|
|
p1->len += p2->len;
|
|
|
|
|
p1->u.str8[p1->len] = '\0';
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 16:01:40 +01:00
|
|
|
static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2)
|
|
|
|
|
{
|
|
|
|
|
JSValue ret;
|
|
|
|
|
JSString *p1, *p2;
|
|
|
|
|
p1 = JS_VALUE_GET_STRING(op1);
|
|
|
|
|
if (JS_ConcatStringInPlace(ctx, p1, op2)) {
|
|
|
|
|
JS_FreeValue(ctx, op2);
|
|
|
|
|
return op1;
|
|
|
|
|
}
|
|
|
|
|
p2 = JS_VALUE_GET_STRING(op2);
|
|
|
|
|
ret = JS_ConcatString1(ctx, p1, p2);
|
|
|
|
|
JS_FreeValue(ctx, op1);
|
|
|
|
|
JS_FreeValue(ctx, op2);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the character at position 'idx'. 'val' must be a string or rope */
|
|
|
|
|
static int string_rope_get(JSValueConst val, uint32_t idx)
|
|
|
|
|
{
|
|
|
|
|
if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
|
|
|
|
|
return string_get(JS_VALUE_GET_STRING(val), idx);
|
|
|
|
|
} else {
|
|
|
|
|
JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
|
|
|
|
|
uint32_t len;
|
|
|
|
|
if (JS_VALUE_GET_TAG(r->left) == JS_TAG_STRING)
|
|
|
|
|
len = JS_VALUE_GET_STRING(r->left)->len;
|
|
|
|
|
else
|
|
|
|
|
len = JS_VALUE_GET_STRING_ROPE(r->left)->len;
|
|
|
|
|
if (idx < len)
|
|
|
|
|
return string_rope_get(r->left, idx);
|
|
|
|
|
else
|
|
|
|
|
return string_rope_get(r->right, idx - len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
JSValueConst stack[JS_STRING_ROPE_MAX_DEPTH];
|
|
|
|
|
int stack_len;
|
|
|
|
|
} JSStringRopeIter;
|
|
|
|
|
|
|
|
|
|
static void string_rope_iter_init(JSStringRopeIter *s, JSValueConst val)
|
|
|
|
|
{
|
|
|
|
|
s->stack_len = 0;
|
|
|
|
|
s->stack[s->stack_len++] = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* iterate thru a rope and return the strings in order */
|
|
|
|
|
static JSString *string_rope_iter_next(JSStringRopeIter *s)
|
|
|
|
|
{
|
|
|
|
|
JSValueConst val;
|
|
|
|
|
JSStringRope *r;
|
|
|
|
|
|
|
|
|
|
if (s->stack_len == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
val = s->stack[--s->stack_len];
|
|
|
|
|
for(;;) {
|
|
|
|
|
if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING)
|
|
|
|
|
return JS_VALUE_GET_STRING(val);
|
|
|
|
|
r = JS_VALUE_GET_STRING_ROPE(val);
|
|
|
|
|
assert(s->stack_len < JS_STRING_ROPE_MAX_DEPTH);
|
|
|
|
|
s->stack[s->stack_len++] = r->right;
|
|
|
|
|
val = r->left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t string_rope_get_len(JSValueConst val)
|
|
|
|
|
{
|
|
|
|
|
if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING)
|
|
|
|
|
return JS_VALUE_GET_STRING(val)->len;
|
|
|
|
|
else
|
|
|
|
|
return JS_VALUE_GET_STRING_ROPE(val)->len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int js_string_rope_compare(JSContext *ctx, JSValueConst op1,
|
|
|
|
|
JSValueConst op2, BOOL eq_only)
|
|
|
|
|
{
|
|
|
|
|
uint32_t len1, len2, len, pos1, pos2, l;
|
|
|
|
|
int res;
|
|
|
|
|
JSStringRopeIter it1, it2;
|
|
|
|
|
JSString *p1, *p2;
|
|
|
|
|
|
|
|
|
|
len1 = string_rope_get_len(op1);
|
|
|
|
|
len2 = string_rope_get_len(op2);
|
|
|
|
|
/* no need to go further for equality test if
|
|
|
|
|
different length */
|
|
|
|
|
if (eq_only && len1 != len2)
|
|
|
|
|
return 1;
|
|
|
|
|
len = min_uint32(len1, len2);
|
|
|
|
|
string_rope_iter_init(&it1, op1);
|
|
|
|
|
string_rope_iter_init(&it2, op2);
|
|
|
|
|
p1 = string_rope_iter_next(&it1);
|
|
|
|
|
p2 = string_rope_iter_next(&it2);
|
|
|
|
|
pos1 = 0;
|
|
|
|
|
pos2 = 0;
|
|
|
|
|
while (len != 0) {
|
|
|
|
|
l = min_uint32(p1->len - pos1, p2->len - pos2);
|
|
|
|
|
l = min_uint32(l, len);
|
|
|
|
|
res = js_string_memcmp(p1, pos1, p2, pos2, l);
|
|
|
|
|
if (res != 0)
|
|
|
|
|
return res;
|
|
|
|
|
len -= l;
|
|
|
|
|
pos1 += l;
|
|
|
|
|
if (pos1 >= p1->len) {
|
|
|
|
|
p1 = string_rope_iter_next(&it1);
|
|
|
|
|
pos1 = 0;
|
|
|
|
|
}
|
|
|
|
|
pos2 += l;
|
|
|
|
|
if (pos2 >= p2->len) {
|
|
|
|
|
p2 = string_rope_iter_next(&it2);
|
|
|
|
|
pos2 = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (len1 == len2)
|
|
|
|
|
res = 0;
|
|
|
|
|
else if (len1 < len2)
|
|
|
|
|
res = -1;
|
|
|
|
|
else
|
|
|
|
|
res = 1;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 'rope' must be a rope. return a string and modify the rope so that
|
|
|
|
|
it won't need to be linearized again. */
|
|
|
|
|
static JSValue js_linearize_string_rope(JSContext *ctx, JSValue rope)
|
|
|
|
|
{
|
|
|
|
|
StringBuffer b_s, *b = &b_s;
|
|
|
|
|
JSStringRope *r;
|
|
|
|
|
JSValue ret;
|
|
|
|
|
|
|
|
|
|
r = JS_VALUE_GET_STRING_ROPE(rope);
|
|
|
|
|
|
|
|
|
|
/* check whether it is already linearized */
|
|
|
|
|
if (JS_VALUE_GET_TAG(r->right) == JS_TAG_STRING &&
|
|
|
|
|
JS_VALUE_GET_STRING(r->right)->len == 0) {
|
|
|
|
|
ret = JS_DupValue(ctx, r->left);
|
|
|
|
|
JS_FreeValue(ctx, rope);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
if (string_buffer_init2(ctx, b, r->len, r->is_wide_char))
|
|
|
|
|
goto fail;
|
|
|
|
|
if (string_buffer_concat_value(b, rope))
|
|
|
|
|
goto fail;
|
|
|
|
|
ret = string_buffer_end(b);
|
|
|
|
|
if (r->header.ref_count > 1) {
|
|
|
|
|
/* update the rope so that it won't need to be linearized again */
|
|
|
|
|
JS_FreeValue(ctx, r->left);
|
|
|
|
|
JS_FreeValue(ctx, r->right);
|
|
|
|
|
r->left = JS_DupValue(ctx, ret);
|
|
|
|
|
r->right = JS_AtomToString(ctx, JS_ATOM_empty_string);
|
|
|
|
|
}
|
|
|
|
|
JS_FreeValue(ctx, rope);
|
|
|
|
|
return ret;
|
|
|
|
|
fail:
|
|
|
|
|
JS_FreeValue(ctx, rope);
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope);
|
|
|
|
|
|
|
|
|
|
/* op1 and op2 must be strings or string ropes */
|
|
|
|
|
static JSValue js_new_string_rope(JSContext *ctx, JSValue op1, JSValue op2)
|
|
|
|
|
{
|
|
|
|
|
uint32_t len;
|
|
|
|
|
int is_wide_char, depth;
|
|
|
|
|
JSStringRope *r;
|
|
|
|
|
JSValue res;
|
|
|
|
|
|
|
|
|
|
if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
|
|
|
|
|
JSString *p1 = JS_VALUE_GET_STRING(op1);
|
|
|
|
|
len = p1->len;
|
|
|
|
|
is_wide_char = p1->is_wide_char;
|
|
|
|
|
depth = 0;
|
|
|
|
|
} else {
|
|
|
|
|
JSStringRope *r1 = JS_VALUE_GET_STRING_ROPE(op1);
|
|
|
|
|
len = r1->len;
|
|
|
|
|
is_wide_char = r1->is_wide_char;
|
|
|
|
|
depth = r1->depth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
|
|
|
|
|
JSString *p2 = JS_VALUE_GET_STRING(op2);
|
|
|
|
|
len += p2->len;
|
|
|
|
|
is_wide_char |= p2->is_wide_char;
|
|
|
|
|
} else {
|
|
|
|
|
JSStringRope *r2 = JS_VALUE_GET_STRING_ROPE(op2);
|
|
|
|
|
len += r2->len;
|
|
|
|
|
is_wide_char |= r2->is_wide_char;
|
|
|
|
|
depth = max_int(depth, r2->depth);
|
|
|
|
|
}
|
|
|
|
|
if (len > JS_STRING_LEN_MAX) {
|
|
|
|
|
JS_ThrowInternalError(ctx, "string too long");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
r = js_malloc(ctx, sizeof(*r));
|
|
|
|
|
if (!r)
|
|
|
|
|
goto fail;
|
|
|
|
|
r->header.ref_count = 1;
|
|
|
|
|
r->len = len;
|
|
|
|
|
r->is_wide_char = is_wide_char;
|
|
|
|
|
r->depth = depth + 1;
|
|
|
|
|
r->left = op1;
|
|
|
|
|
r->right = op2;
|
|
|
|
|
res = JS_MKPTR(JS_TAG_STRING_ROPE, r);
|
|
|
|
|
if (r->depth > JS_STRING_ROPE_MAX_DEPTH) {
|
|
|
|
|
JSValue res2;
|
|
|
|
|
#ifdef DUMP_ROPE_REBALANCE
|
|
|
|
|
printf("rebalance: initial depth=%d\n", r->depth);
|
|
|
|
|
#endif
|
|
|
|
|
res2 = js_rebalancee_string_rope(ctx, res);
|
|
|
|
|
#ifdef DUMP_ROPE_REBALANCE
|
|
|
|
|
if (JS_VALUE_GET_TAG(res2) == JS_TAG_STRING_ROPE)
|
|
|
|
|
printf("rebalance: final depth=%d\n", JS_VALUE_GET_STRING_ROPE(res2)->depth);
|
|
|
|
|
#endif
|
|
|
|
|
JS_FreeValue(ctx, res);
|
|
|
|
|
return res2;
|
|
|
|
|
} else {
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
fail:
|
|
|
|
|
JS_FreeValue(ctx, op1);
|
|
|
|
|
JS_FreeValue(ctx, op2);
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define ROPE_N_BUCKETS 44
|
|
|
|
|
|
|
|
|
|
/* Fibonacii numbers starting from F_2 */
|
|
|
|
|
static const uint32_t rope_bucket_len[ROPE_N_BUCKETS] = {
|
|
|
|
|
1, 2, 3, 5,
|
|
|
|
|
8, 13, 21, 34,
|
|
|
|
|
55, 89, 144, 233,
|
|
|
|
|
377, 610, 987, 1597,
|
|
|
|
|
2584, 4181, 6765, 10946,
|
|
|
|
|
17711, 28657, 46368, 75025,
|
|
|
|
|
121393, 196418, 317811, 514229,
|
|
|
|
|
832040, 1346269, 2178309, 3524578,
|
|
|
|
|
5702887, 9227465, 14930352, 24157817,
|
|
|
|
|
39088169, 63245986, 102334155, 165580141,
|
|
|
|
|
267914296, 433494437, 701408733, 1134903170, /* > JS_STRING_LEN_MAX */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int js_rebalancee_string_rope_rec(JSContext *ctx, JSValue *buckets,
|
|
|
|
|
JSValueConst val)
|
|
|
|
|
{
|
|
|
|
|
if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
|
|
|
|
|
JSString *p = JS_VALUE_GET_STRING(val);
|
|
|
|
|
uint32_t len, i;
|
|
|
|
|
JSValue a, b;
|
|
|
|
|
|
|
|
|
|
len = p->len;
|
|
|
|
|
if (len == 0)
|
|
|
|
|
return 0; /* nothing to do */
|
|
|
|
|
/* find the bucket i so that rope_bucket_len[i] <= len <
|
|
|
|
|
rope_bucket_len[i + 1] and concatenate the ropes in the
|
|
|
|
|
buckets before */
|
|
|
|
|
a = JS_NULL;
|
|
|
|
|
i = 0;
|
|
|
|
|
while (len >= rope_bucket_len[i + 1]) {
|
|
|
|
|
b = buckets[i];
|
|
|
|
|
if (!JS_IsNull(b)) {
|
|
|
|
|
buckets[i] = JS_NULL;
|
|
|
|
|
if (JS_IsNull(a)) {
|
|
|
|
|
a = b;
|
|
|
|
|
} else {
|
|
|
|
|
a = js_new_string_rope(ctx, b, a);
|
|
|
|
|
if (JS_IsException(a))
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
if (!JS_IsNull(a)) {
|
|
|
|
|
a = js_new_string_rope(ctx, a, JS_DupValue(ctx, val));
|
|
|
|
|
if (JS_IsException(a))
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
a = JS_DupValue(ctx, val);
|
|
|
|
|
}
|
|
|
|
|
while (!JS_IsNull(buckets[i])) {
|
|
|
|
|
a = js_new_string_rope(ctx, buckets[i], a);
|
|
|
|
|
buckets[i] = JS_NULL;
|
|
|
|
|
if (JS_IsException(a))
|
|
|
|
|
return -1;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
buckets[i] = a;
|
|
|
|
|
} else {
|
|
|
|
|
JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
|
|
|
|
|
js_rebalancee_string_rope_rec(ctx, buckets, r->left);
|
|
|
|
|
js_rebalancee_string_rope_rec(ctx, buckets, r->right);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a new rope which is balanced. Algorithm from "Ropes: an
|
|
|
|
|
Alternative to Strings", Hans-J. Boehm, Russ Atkinson and Michael
|
|
|
|
|
Plass. */
|
|
|
|
|
static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope)
|
|
|
|
|
{
|
|
|
|
|
JSValue buckets[ROPE_N_BUCKETS], a, b;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < ROPE_N_BUCKETS; i++)
|
|
|
|
|
buckets[i] = JS_NULL;
|
|
|
|
|
if (js_rebalancee_string_rope_rec(ctx, buckets, rope))
|
|
|
|
|
goto fail;
|
|
|
|
|
a = JS_NULL;
|
|
|
|
|
for(i = 0; i < ROPE_N_BUCKETS; i++) {
|
|
|
|
|
b = buckets[i];
|
|
|
|
|
if (!JS_IsNull(b)) {
|
|
|
|
|
buckets[i] = JS_NULL;
|
|
|
|
|
if (JS_IsNull(a)) {
|
|
|
|
|
a = b;
|
|
|
|
|
} else {
|
|
|
|
|
a = js_new_string_rope(ctx, b, a);
|
|
|
|
|
if (JS_IsException(a))
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* fail safe */
|
|
|
|
|
if (JS_IsNull(a))
|
|
|
|
|
return JS_AtomToString(ctx, JS_ATOM_empty_string);
|
|
|
|
|
else
|
|
|
|
|
return a;
|
|
|
|
|
fail:
|
|
|
|
|
for(i = 0; i < ROPE_N_BUCKETS; i++) {
|
|
|
|
|
JS_FreeValue(ctx, buckets[i]);
|
|
|
|
|
}
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-11 12:49:40 +01:00
|
|
|
/* op1 and op2 are converted to strings. For convenience, op1 or op2 =
|
2020-09-06 18:53:08 +02:00
|
|
|
JS_EXCEPTION are accepted and return JS_EXCEPTION. */
|
|
|
|
|
static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
|
|
|
|
|
{
|
|
|
|
|
JSString *p1, *p2;
|
|
|
|
|
|
2025-03-25 16:01:40 +01:00
|
|
|
if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING &&
|
|
|
|
|
JS_VALUE_GET_TAG(op1) != JS_TAG_STRING_ROPE)) {
|
2020-09-06 18:53:08 +02:00
|
|
|
op1 = JS_ToStringFree(ctx, op1);
|
|
|
|
|
if (JS_IsException(op1)) {
|
|
|
|
|
JS_FreeValue(ctx, op2);
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-25 16:01:40 +01:00
|
|
|
if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING &&
|
|
|
|
|
JS_VALUE_GET_TAG(op2) != JS_TAG_STRING_ROPE)) {
|
2020-09-06 18:53:08 +02:00
|
|
|
op2 = JS_ToStringFree(ctx, op2);
|
|
|
|
|
if (JS_IsException(op2)) {
|
|
|
|
|
JS_FreeValue(ctx, op1);
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-25 16:01:40 +01:00
|
|
|
|
|
|
|
|
/* normal concatenation for short strings */
|
|
|
|
|
if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
|
|
|
|
|
p2 = JS_VALUE_GET_STRING(op2);
|
|
|
|
|
if (p2->len == 0) {
|
|
|
|
|
JS_FreeValue(ctx, op2);
|
|
|
|
|
return op1;
|
|
|
|
|
}
|
|
|
|
|
if (p2->len <= JS_STRING_ROPE_SHORT_LEN) {
|
|
|
|
|
if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
|
|
|
|
|
p1 = JS_VALUE_GET_STRING(op1);
|
|
|
|
|
if (p1->len <= JS_STRING_ROPE_SHORT2_LEN) {
|
|
|
|
|
return JS_ConcatString2(ctx, op1, op2);
|
|
|
|
|
} else {
|
|
|
|
|
return js_new_string_rope(ctx, op1, op2);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
JSStringRope *r1;
|
|
|
|
|
r1 = JS_VALUE_GET_STRING_ROPE(op1);
|
|
|
|
|
if (JS_VALUE_GET_TAG(r1->right) == JS_TAG_STRING &&
|
|
|
|
|
JS_VALUE_GET_STRING(r1->right)->len <= JS_STRING_ROPE_SHORT_LEN) {
|
|
|
|
|
JSValue val, ret;
|
|
|
|
|
val = JS_ConcatString2(ctx, JS_DupValue(ctx, r1->right), op2);
|
|
|
|
|
if (JS_IsException(val)) {
|
|
|
|
|
JS_FreeValue(ctx, op1);
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
ret = js_new_string_rope(ctx, JS_DupValue(ctx, r1->left), val);
|
|
|
|
|
JS_FreeValue(ctx, op1);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
|
|
|
|
|
JSStringRope *r2;
|
|
|
|
|
p1 = JS_VALUE_GET_STRING(op1);
|
|
|
|
|
if (p1->len == 0) {
|
|
|
|
|
JS_FreeValue(ctx, op1);
|
|
|
|
|
return op2;
|
|
|
|
|
}
|
|
|
|
|
r2 = JS_VALUE_GET_STRING_ROPE(op2);
|
|
|
|
|
if (JS_VALUE_GET_TAG(r2->left) == JS_TAG_STRING &&
|
|
|
|
|
JS_VALUE_GET_STRING(r2->left)->len <= JS_STRING_ROPE_SHORT_LEN) {
|
|
|
|
|
JSValue val, ret;
|
|
|
|
|
val = JS_ConcatString2(ctx, op1, JS_DupValue(ctx, r2->left));
|
|
|
|
|
if (JS_IsException(val)) {
|
|
|
|
|
JS_FreeValue(ctx, op2);
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
ret = js_new_string_rope(ctx, val, JS_DupValue(ctx, r2->right));
|
|
|
|
|
JS_FreeValue(ctx, op2);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
2025-03-25 16:01:40 +01:00
|
|
|
return js_new_string_rope(ctx, op1, op2);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Shape support */
|
|
|
|
|
|
|
|
|
|
static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
|
|
|
|
|
{
|
|
|
|
|
return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
|
|
|
|
|
prop_size * sizeof(JSShapeProperty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
|
|
|
|
|
{
|
|
|
|
|
return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:10:15 +02:00
|
|
|
static inline uint32_t *prop_hash_end(JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
return (uint32_t *)sh;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
static inline void *get_alloc_from_shape(JSShape *sh)
|
|
|
|
|
{
|
2020-09-06 19:10:15 +02:00
|
|
|
return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline JSShapeProperty *get_shape_prop(JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
return sh->prop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int init_shape_hash(JSRuntime *rt)
|
|
|
|
|
{
|
|
|
|
|
rt->shape_hash_bits = 4; /* 16 shapes */
|
|
|
|
|
rt->shape_hash_size = 1 << rt->shape_hash_bits;
|
|
|
|
|
rt->shape_hash_count = 0;
|
|
|
|
|
rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
|
|
|
|
|
rt->shape_hash_size);
|
|
|
|
|
if (!rt->shape_hash)
|
|
|
|
|
return -1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* same magic hash multiplier as the Linux kernel */
|
|
|
|
|
static uint32_t shape_hash(uint32_t h, uint32_t val)
|
|
|
|
|
{
|
|
|
|
|
return (h + val) * 0x9e370001;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* truncate the shape hash to 'hash_bits' bits */
|
|
|
|
|
static uint32_t get_shape_hash(uint32_t h, int hash_bits)
|
|
|
|
|
{
|
|
|
|
|
return h >> (32 - hash_bits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint32_t shape_initial_hash(JSObject *proto)
|
|
|
|
|
{
|
|
|
|
|
uint32_t h;
|
|
|
|
|
h = shape_hash(1, (uintptr_t)proto);
|
|
|
|
|
if (sizeof(proto) > 4)
|
|
|
|
|
h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
|
|
|
|
|
{
|
|
|
|
|
int new_shape_hash_size, i;
|
|
|
|
|
uint32_t h;
|
|
|
|
|
JSShape **new_shape_hash, *sh, *sh_next;
|
|
|
|
|
|
|
|
|
|
new_shape_hash_size = 1 << new_shape_hash_bits;
|
|
|
|
|
new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
|
|
|
|
|
new_shape_hash_size);
|
|
|
|
|
if (!new_shape_hash)
|
|
|
|
|
return -1;
|
|
|
|
|
for(i = 0; i < rt->shape_hash_size; i++) {
|
|
|
|
|
for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
|
|
|
|
|
sh_next = sh->shape_hash_next;
|
|
|
|
|
h = get_shape_hash(sh->hash, new_shape_hash_bits);
|
|
|
|
|
sh->shape_hash_next = new_shape_hash[h];
|
|
|
|
|
new_shape_hash[h] = sh;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
js_free_rt(rt, rt->shape_hash);
|
|
|
|
|
rt->shape_hash_bits = new_shape_hash_bits;
|
|
|
|
|
rt->shape_hash_size = new_shape_hash_size;
|
|
|
|
|
rt->shape_hash = new_shape_hash;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
uint32_t h;
|
|
|
|
|
h = get_shape_hash(sh->hash, rt->shape_hash_bits);
|
|
|
|
|
sh->shape_hash_next = rt->shape_hash[h];
|
|
|
|
|
rt->shape_hash[h] = sh;
|
|
|
|
|
rt->shape_hash_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
uint32_t h;
|
|
|
|
|
JSShape **psh;
|
|
|
|
|
|
|
|
|
|
h = get_shape_hash(sh->hash, rt->shape_hash_bits);
|
|
|
|
|
psh = &rt->shape_hash[h];
|
|
|
|
|
while (*psh != sh)
|
|
|
|
|
psh = &(*psh)->shape_hash_next;
|
|
|
|
|
*psh = sh->shape_hash_next;
|
|
|
|
|
rt->shape_hash_count--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* create a new empty shape with prototype 'proto' */
|
|
|
|
|
static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
|
|
|
|
|
int hash_size, int prop_size)
|
|
|
|
|
{
|
|
|
|
|
JSRuntime *rt = ctx->rt;
|
|
|
|
|
void *sh_alloc;
|
|
|
|
|
JSShape *sh;
|
|
|
|
|
|
|
|
|
|
/* resize the shape hash table if necessary */
|
|
|
|
|
if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
|
|
|
|
|
resize_shape_hash(rt, rt->shape_hash_bits + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
|
|
|
|
|
if (!sh_alloc)
|
|
|
|
|
return NULL;
|
|
|
|
|
sh = get_shape_from_alloc(sh_alloc, hash_size);
|
|
|
|
|
sh->header.ref_count = 1;
|
|
|
|
|
add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
|
|
|
|
|
if (proto)
|
|
|
|
|
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
|
|
|
|
|
sh->proto = proto;
|
2020-09-06 19:10:15 +02:00
|
|
|
memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
|
2020-09-06 18:53:08 +02:00
|
|
|
hash_size);
|
|
|
|
|
sh->prop_hash_mask = hash_size - 1;
|
|
|
|
|
sh->prop_size = prop_size;
|
2020-09-06 19:07:30 +02:00
|
|
|
sh->prop_count = 0;
|
|
|
|
|
sh->deleted_prop_count = 0;
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
/* insert in the hash table */
|
|
|
|
|
sh->hash = shape_initial_hash(proto);
|
|
|
|
|
sh->is_hashed = TRUE;
|
|
|
|
|
sh->has_small_array_index = FALSE;
|
|
|
|
|
js_shape_hash_link(ctx->rt, sh);
|
|
|
|
|
return sh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
|
|
|
|
|
{
|
|
|
|
|
return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
|
|
|
|
|
JS_PROP_INITIAL_SIZE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The shape is cloned. The new shape is not inserted in the shape
|
|
|
|
|
hash table */
|
|
|
|
|
static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
|
|
|
|
|
{
|
|
|
|
|
JSShape *sh;
|
|
|
|
|
void *sh_alloc, *sh_alloc1;
|
|
|
|
|
size_t size;
|
|
|
|
|
JSShapeProperty *pr;
|
|
|
|
|
uint32_t i, hash_size;
|
|
|
|
|
|
|
|
|
|
hash_size = sh1->prop_hash_mask + 1;
|
|
|
|
|
size = get_shape_size(hash_size, sh1->prop_size);
|
|
|
|
|
sh_alloc = js_malloc(ctx, size);
|
|
|
|
|
if (!sh_alloc)
|
|
|
|
|
return NULL;
|
|
|
|
|
sh_alloc1 = get_alloc_from_shape(sh1);
|
|
|
|
|
memcpy(sh_alloc, sh_alloc1, size);
|
|
|
|
|
sh = get_shape_from_alloc(sh_alloc, hash_size);
|
|
|
|
|
sh->header.ref_count = 1;
|
|
|
|
|
add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
|
|
|
|
|
sh->is_hashed = FALSE;
|
|
|
|
|
if (sh->proto) {
|
|
|
|
|
JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
|
|
|
|
|
}
|
|
|
|
|
for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
|
|
|
|
|
JS_DupAtom(ctx, pr->atom);
|
|
|
|
|
}
|
|
|
|
|
return sh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSShape *js_dup_shape(JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
sh->header.ref_count++;
|
|
|
|
|
return sh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void js_free_shape0(JSRuntime *rt, JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
uint32_t i;
|
|
|
|
|
JSShapeProperty *pr;
|
|
|
|
|
|
|
|
|
|
assert(sh->header.ref_count == 0);
|
|
|
|
|
if (sh->is_hashed)
|
|
|
|
|
js_shape_hash_unlink(rt, sh);
|
|
|
|
|
if (sh->proto != NULL) {
|
|
|
|
|
JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
|
|
|
|
|
}
|
|
|
|
|
pr = get_shape_prop(sh);
|
|
|
|
|
for(i = 0; i < sh->prop_count; i++) {
|
|
|
|
|
JS_FreeAtomRT(rt, pr->atom);
|
|
|
|
|
pr++;
|
|
|
|
|
}
|
|
|
|
|
remove_gc_object(&sh->header);
|
|
|
|
|
js_free_rt(rt, get_alloc_from_shape(sh));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void js_free_shape(JSRuntime *rt, JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(--sh->header.ref_count <= 0)) {
|
|
|
|
|
js_free_shape0(rt, sh);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
if (sh)
|
|
|
|
|
js_free_shape(rt, sh);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* make space to hold at least 'count' properties */
|
|
|
|
|
static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
|
|
|
|
|
JSObject *p, uint32_t count)
|
|
|
|
|
{
|
|
|
|
|
JSShape *sh;
|
|
|
|
|
uint32_t new_size, new_hash_size, new_hash_mask, i;
|
|
|
|
|
JSShapeProperty *pr;
|
|
|
|
|
void *sh_alloc;
|
|
|
|
|
intptr_t h;
|
2023-12-27 18:19:06 +01:00
|
|
|
JSShape *old_sh;
|
2020-09-06 18:53:08 +02:00
|
|
|
|
|
|
|
|
sh = *psh;
|
|
|
|
|
new_size = max_int(count, sh->prop_size * 3 / 2);
|
|
|
|
|
/* Reallocate prop array first to avoid crash or size inconsistency
|
|
|
|
|
in case of memory allocation failure */
|
|
|
|
|
if (p) {
|
|
|
|
|
JSProperty *new_prop;
|
|
|
|
|
new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
|
|
|
|
|
if (unlikely(!new_prop))
|
|
|
|
|
return -1;
|
|
|
|
|
p->prop = new_prop;
|
|
|
|
|
}
|
|
|
|
|
new_hash_size = sh->prop_hash_mask + 1;
|
|
|
|
|
while (new_hash_size < new_size)
|
|
|
|
|
new_hash_size = 2 * new_hash_size;
|
2023-12-27 18:19:06 +01:00
|
|
|
/* resize the property shapes. Using js_realloc() is not possible in
|
|
|
|
|
case the GC runs during the allocation */
|
|
|
|
|
old_sh = sh;
|
|
|
|
|
sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
|
|
|
|
|
if (!sh_alloc)
|
|
|
|
|
return -1;
|
|
|
|
|
sh = get_shape_from_alloc(sh_alloc, new_hash_size);
|
|
|
|
|
list_del(&old_sh->header.link);
|
|
|
|
|
/* copy all the shape properties */
|
|
|
|
|
memcpy(sh, old_sh,
|
|
|
|
|
sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
|
|
|
|
|
list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
if (new_hash_size != (sh->prop_hash_mask + 1)) {
|
|
|
|
|
/* resize the hash table and the properties */
|
|
|
|
|
new_hash_mask = new_hash_size - 1;
|
|
|
|
|
sh->prop_hash_mask = new_hash_mask;
|
2020-09-06 19:10:15 +02:00
|
|
|
memset(prop_hash_end(sh) - new_hash_size, 0,
|
|
|
|
|
sizeof(prop_hash_end(sh)[0]) * new_hash_size);
|
2020-09-06 18:53:08 +02:00
|
|
|
for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
|
|
|
|
|
if (pr->atom != JS_ATOM_NULL) {
|
|
|
|
|
h = ((uintptr_t)pr->atom & new_hash_mask);
|
2020-09-06 19:10:15 +02:00
|
|
|
pr->hash_next = prop_hash_end(sh)[-h - 1];
|
|
|
|
|
prop_hash_end(sh)[-h - 1] = i + 1;
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2023-12-27 18:19:06 +01:00
|
|
|
/* just copy the previous hash table */
|
|
|
|
|
memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size,
|
|
|
|
|
sizeof(prop_hash_end(sh)[0]) * new_hash_size);
|
2020-09-06 18:53:08 +02:00
|
|
|
}
|
2023-12-27 18:19:06 +01:00
|
|
|
js_free(ctx, get_alloc_from_shape(old_sh));
|
2020-09-06 18:53:08 +02:00
|
|
|
*psh = sh;
|
|
|
|
|
sh->prop_size = new_size;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 19:07:30 +02:00
|
|
|
/* remove the deleted properties. */
|
|
|
|
|
static int compact_properties(JSContext *ctx, JSObject *p)
|
|
|
|
|
{
|
|
|
|
|
JSShape *sh, *old_sh;
|
|
|
|
|
void *sh_alloc;
|
|
|
|
|
intptr_t h;
|
|
|
|
|
uint32_t new_hash_size, i, j, new_hash_mask, new_size;
|
|
|
|
|
JSShapeProperty *old_pr, *pr;
|
|
|
|
|
JSProperty *prop, *new_prop;
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 19:07:30 +02:00
|
|
|
sh = p->shape;
|
|
|
|
|
assert(!sh->is_hashed);
|
|
|
|
|
|
|
|
|
|
new_size = max_int(JS_PROP_INITIAL_SIZE,
|
|
|
|
|
sh->prop_count - sh->deleted_prop_count);
|
|
|
|
|
assert(new_size <= sh->prop_size);
|
|
|
|
|
|
|
|
|
|
new_hash_size = sh->prop_hash_mask + 1;
|
|
|
|
|
while ((new_hash_size / 2) >= new_size)
|
|
|
|
|
new_hash_size = new_hash_size / 2;
|
|
|
|
|
new_hash_mask = new_hash_size - 1;
|
|
|
|
|
|
|
|
|
|
/* resize the hash table and the properties */
|
|
|
|
|
old_sh = sh;
|
|
|
|
|
sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
|
|
|
|
|
if (!sh_alloc)
|
|
|
|
|
return -1;
|
|
|
|
|
sh = get_shape_from_alloc(sh_alloc, new_hash_size);
|
|
|
|
|
list_del(&old_sh->header.link);
|
|
|
|
|
memcpy(sh, old_sh, sizeof(JSShape));
|
|
|
|
|
list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 19:10:15 +02:00
|
|
|
memset(prop_hash_end(sh) - new_hash_size, 0,
|
|
|
|
|
sizeof(prop_hash_end(sh)[0]) * new_hash_size);
|
2020-09-06 19:07:30 +02:00
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
|
old_pr = old_sh->prop;
|
|
|
|
|
pr = sh->prop;
|
|
|
|
|
prop = p->prop;
|
|
|
|
|
for(i = 0; i < sh->prop_count; i++) {
|
|
|
|
|
if (old_pr->atom != JS_ATOM_NULL) {
|
|
|
|
|
pr->atom = old_pr->atom;
|
|
|
|
|
pr->flags = old_pr->flags;
|
|
|
|
|
h = ((uintptr_t)old_pr->atom & new_hash_mask);
|
2020-09-06 19:10:15 +02:00
|
|
|
pr->hash_next = prop_hash_end(sh)[-h - 1];
|
|
|
|
|
prop_hash_end(sh)[-h - 1] = j + 1;
|
2020-09-06 19:07:30 +02:00
|
|
|
prop[j] = prop[i];
|
|
|
|
|
j++;
|
|
|
|
|
pr++;
|
|
|
|
|
}
|
|
|
|
|
old_pr++;
|
|
|
|
|
}
|
|
|
|
|
assert(j == (sh->prop_count - sh->deleted_prop_count));
|
|
|
|
|
sh->prop_hash_mask = new_hash_mask;
|
|
|
|
|
sh->prop_size = new_size;
|
|
|
|
|
sh->deleted_prop_count = 0;
|
|
|
|
|
sh->prop_count = j;
|
|
|
|
|
|
|
|
|
|
p->shape = sh;
|
|
|
|
|
js_free(ctx, get_alloc_from_shape(old_sh));
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 19:07:30 +02:00
|
|
|
/* reduce the size of the object properties */
|
|
|
|
|
new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
|
|
|
|
|
if (new_prop)
|
|
|
|
|
p->prop = new_prop;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
static int add_shape_property(JSContext *ctx, JSShape **psh,
|
|
|
|
|
JSObject *p, JSAtom atom, int prop_flags)
|
|
|
|
|
{
|
|
|
|
|
JSRuntime *rt = ctx->rt;
|
|
|
|
|
JSShape *sh = *psh;
|
|
|
|
|
JSShapeProperty *pr, *prop;
|
|
|
|
|
uint32_t hash_mask, new_shape_hash = 0;
|
|
|
|
|
intptr_t h;
|
|
|
|
|
|
|
|
|
|
/* update the shape hash */
|
|
|
|
|
if (sh->is_hashed) {
|
|
|
|
|
js_shape_hash_unlink(rt, sh);
|
|
|
|
|
new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (unlikely(sh->prop_count >= sh->prop_size)) {
|
|
|
|
|
if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
|
|
|
|
|
/* in case of error, reinsert in the hash table.
|
|
|
|
|
sh is still valid if resize_properties() failed */
|
|
|
|
|
if (sh->is_hashed)
|
|
|
|
|
js_shape_hash_link(rt, sh);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
sh = *psh;
|
|
|
|
|
}
|
|
|
|
|
if (sh->is_hashed) {
|
|
|
|
|
sh->hash = new_shape_hash;
|
|
|
|
|
js_shape_hash_link(rt, sh);
|
|
|
|
|
}
|
|
|
|
|
/* Initialize the new shape property.
|
|
|
|
|
The object property at p->prop[sh->prop_count] is uninitialized */
|
|
|
|
|
prop = get_shape_prop(sh);
|
|
|
|
|
pr = &prop[sh->prop_count++];
|
|
|
|
|
pr->atom = JS_DupAtom(ctx, atom);
|
|
|
|
|
pr->flags = prop_flags;
|
|
|
|
|
sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
|
|
|
|
|
/* add in hash table */
|
|
|
|
|
hash_mask = sh->prop_hash_mask;
|
|
|
|
|
h = atom & hash_mask;
|
2020-09-06 19:10:15 +02:00
|
|
|
pr->hash_next = prop_hash_end(sh)[-h - 1];
|
|
|
|
|
prop_hash_end(sh)[-h - 1] = sh->prop_count;
|
2020-09-06 18:53:08 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* find a hashed empty shape matching the prototype. Return NULL if
|
|
|
|
|
not found */
|
|
|
|
|
static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
|
|
|
|
|
{
|
|
|
|
|
JSShape *sh1;
|
|
|
|
|
uint32_t h, h1;
|
|
|
|
|
|
|
|
|
|
h = shape_initial_hash(proto);
|
|
|
|
|
h1 = get_shape_hash(h, rt->shape_hash_bits);
|
|
|
|
|
for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
|
|
|
|
|
if (sh1->hash == h &&
|
|
|
|
|
sh1->proto == proto &&
|
|
|
|
|
sh1->prop_count == 0) {
|
|
|
|
|
return sh1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
|
|
|
|
|
not found */
|
|
|
|
|
static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
|
|
|
|
|
JSAtom atom, int prop_flags)
|
|
|
|
|
{
|
|
|
|
|
JSShape *sh1;
|
|
|
|
|
uint32_t h, h1, i, n;
|
|
|
|
|
|
|
|
|
|
h = sh->hash;
|
|
|
|
|
h = shape_hash(h, atom);
|
|
|
|
|
h = shape_hash(h, prop_flags);
|
|
|
|
|
h1 = get_shape_hash(h, rt->shape_hash_bits);
|
|
|
|
|
for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
|
|
|
|
|
/* we test the hash first so that the rest is done only if the
|
|
|
|
|
shapes really match */
|
|
|
|
|
if (sh1->hash == h &&
|
|
|
|
|
sh1->proto == sh->proto &&
|
|
|
|
|
sh1->prop_count == ((n = sh->prop_count) + 1)) {
|
|
|
|
|
for(i = 0; i < n; i++) {
|
|
|
|
|
if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
|
|
|
|
|
unlikely(sh1->prop[i].flags != sh->prop[i].flags))
|
|
|
|
|
goto next;
|
|
|
|
|
}
|
|
|
|
|
if (unlikely(sh1->prop[n].atom != atom) ||
|
|
|
|
|
unlikely(sh1->prop[n].flags != prop_flags))
|
|
|
|
|
goto next;
|
|
|
|
|
return sh1;
|
|
|
|
|
}
|
|
|
|
|
next: ;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
|
|
|
|
|
{
|
|
|
|
|
char atom_buf[ATOM_GET_STR_BUF_SIZE];
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
/* XXX: should output readable class prototype */
|
|
|
|
|
printf("%5d %3d%c %14p %5d %5d", i,
|
|
|
|
|
sh->header.ref_count, " *"[sh->is_hashed],
|
|
|
|
|
(void *)sh->proto, sh->prop_size, sh->prop_count);
|
|
|
|
|
for(j = 0; j < sh->prop_count; j++) {
|
|
|
|
|
printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
|
|
|
|
|
sh->prop[j].atom));
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
JSShape *sh;
|
|
|
|
|
struct list_head *el;
|
|
|
|
|
JSObject *p;
|
|
|
|
|
JSGCObjectHeader *gp;
|
2024-02-10 16:18:11 +01:00
|
|
|
|
2020-09-06 18:53:08 +02:00
|
|
|
printf("JSShapes: {\n");
|
|
|
|
|
printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
|
|
|
|
|
for(i = 0; i < rt->shape_hash_size; i++) {
|
|
|
|
|
for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
|
|
|
|
|
JS_DumpShape(rt, i, sh);
|
|
|
|
|
assert(sh->is_hashed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* dump non-hashed shapes */
|
|
|
|
|
list_for_each(el, &rt->gc_obj_list) {
|
|
|
|
|
gp = list_entry(el, JSGCObjectHeader, link);
|
|
|
|
|
if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
|
|
|
|
|
p = (JSObject *)gp;
|
|
|
|
|
if (!p->shape->is_hashed) {
|
|
|
|
|
JS_DumpShape(rt, -1, p->shape);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf("}\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
|
|
|
|
|
{
|
|
|
|
|
JSObject *p;
|
|
|
|
|
|
|
|
|
|
js_trigger_gc(ctx->rt, sizeof(JSObject));
|
|
|
|
|
p = js_malloc(ctx, sizeof(JSObject));
|
|
|
|
|
if (unlikely(!p))
|
|
|
|
|
goto fail;
|
|
|
|
|
p->class_id = class_id;
|
|
|
|
|
p->extensible = TRUE;
|
|
|
|
|
p->free_mark = 0;
|
|
|
|
|
p->is_exotic = 0;
|
|
|
|
|
p->fast_array = 0;
|
|
|
|
|
p->is_constructor = 0;
|
2025-04-29 19:03:36 +02:00
|
|
|
p->has_immutable_prototype = 0;
|
2020-09-06 18:53:08 +02:00
|
|
|
p->tmp_mark = 0;
|
2020-09-06 19:10:15 +02:00
|
|
|
p->is_HTMLDDA = 0;
|
2025-04-05 12:49:29 +02:00
|
|
|
p->weakref_count = 0;
|
2020-09-06 18:53:08 +02:00
|
|
|
p->u.opaque = NULL;
|
|
|
|
|
p->shape = sh;
|
|
|
|
|
p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
|
|
|
|
|
if (unlikely(!p->prop)) {
|
|
|
|
|
js_free(ctx, p);
|
|
|
|
|
fail:
|
|
|
|
|
js_free_shape(ctx->rt, sh);
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(class_id) {
|
|
|
|
|
case JS_CLASS_OBJECT:
|
|
|
|
|
break;
|
|
|
|
|
case JS_CLASS_ARRAY:
|
|
|
|
|
{
|
|
|
|
|
JSProperty *pr;
|
|
|
|
|
p->is_exotic = 1;
|
|
|
|
|
p->fast_array = 1;
|
|
|
|
|
p->u.array.u.values = NULL;
|
|
|
|
|
p->u.array.count = 0;
|
|
|
|
|
p->u.array.u1.size = 0;
|
|
|
|
|
/* the length property is always the first one */
|
|
|
|
|
if (likely(sh == ctx->array_shape)) {
|
|
|
|
|
pr = &p->prop[0];
|
|
|
|
|
} else {
|
|
|
|
|
/* only used for the first array */
|
|
|
|
|
/* cannot fail */
|
|
|
|
|
pr = add_property(ctx, p, JS_ATOM_length,
|
|
|
|
|
JS_PROP_WRITABLE | JS_PROP_LENGTH);
|
|
|
|
|
}
|
|
|
|
|
pr->u.value = JS_NewInt32(ctx, 0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case JS_CLASS_C_FUNCTION:
|
|
|
|
|
p->prop[0].u.value = JS_UNDEFINED;
|
|
|
|
|
break;
|
|
|
|
|
case JS_CLASS_ARGUMENTS:
|
2020-09-06 19:10:15 +02:00
|
|
|
case JS_CLASS_UINT8C_ARRAY:
|
|
|
|
|
case JS_CLASS_INT8_ARRAY:
|
|
|
|
|
case JS_CLASS_UINT8_ARRAY:
|
|
|
|
|
case JS_CLASS_INT16_ARRAY:
|
|
|
|
|
case JS_CLASS_UINT16_ARRAY:
|
|
|
|
|
case JS_CLASS_INT32_ARRAY:
|
|
|
|
|
case JS_CLASS_UINT32_ARRAY:
|
|
|
|
|
case JS_CLASS_BIG_INT64_ARRAY:
|
|
|
|
|
case JS_CLASS_BIG_UINT64_ARRAY:
|
2025-05-05 19:12:32 +02:00
|
|
|
case JS_CLASS_FLOAT16_ARRAY:
|
2020-09-06 19:10:15 +02:00
|
|
|
case JS_CLASS_FLOAT32_ARRAY:
|
|
|
|
|
case JS_CLASS_FLOAT64_ARRAY:
|
2020-09-06 18:53:08 +02:00
|
|
|
p->is_exotic = 1;
|
|
|
|
|
p->fast_array = 1;
|
|
|
|
|
p->u.array.u.ptr = NULL;
|
|
|
|
|
p->u.array.count |