diff --git a/Changelog b/Changelog index 0c25b96..c09af91 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,10 @@ +2021-03-27: + +- faster Array.prototype.push and Array.prototype.unshift +- added JS_UpdateStackTop() +- fixed Windows console +- misc bug fixes + 2020-11-08: - improved function parameter initializers diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2c8fdeb --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +QuickJS Javascript Engine + +Copyright (c) 2017-2021 Fabrice Bellard +Copyright (c) 2017-2021 Charlie Gordon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile index e6ae827..49b1f6f 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # # QuickJS Javascript Engine # -# Copyright (c) 2017-2020 Fabrice Bellard -# Copyright (c) 2017-2020 Charlie Gordon +# Copyright (c) 2017-2021 Fabrice Bellard +# Copyright (c) 2017-2021 Charlie Gordon # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -179,6 +179,7 @@ LIBS=-lm ifndef CONFIG_WIN32 LIBS+=-ldl -lpthread endif +LIBS+=$(EXTRA_LIBS) $(OBJDIR): mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests diff --git a/TODO b/TODO index b5500c9..2a3b3c3 100644 --- a/TODO +++ b/TODO @@ -66,5 +66,5 @@ Optimization ideas: Test262o: 0/11262 errors, 463 excluded Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch) -Result: 51/75119 errors, 899 excluded, 570 skipped -Test262 commit: 1c33fdb0ca60fb9d7392403be769ed0d26209132 +Result: 35/75280 errors, 909 excluded, 585 skipped +Test262 commit: 31126581e7290f9233c29cefd93f66c6ac78f1c9 diff --git a/VERSION b/VERSION index 78749b0..22ffec1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2020-11-08 +2021-03-27 diff --git a/doc/quickjs.texi b/doc/quickjs.texi index 40c0bb5..9eb6354 100644 --- a/doc/quickjs.texi +++ b/doc/quickjs.texi @@ -896,10 +896,11 @@ set the C opaque point with @code{JS_GetOpaque()}/@code{JS_SetOpaque()}. When defining a new JS class, it is possible to declare a finalizer -which is called when the object is destroyed. A @code{gc_mark} method -can be provided so that the cycle removal algorithm can find the other -objects referenced by this object. Other methods are available to -define exotic object behaviors. +which is called when the object is destroyed. The finalizer should be +used to release C resources. It is invalid to execute JS code from +it. A @code{gc_mark} method can be provided so that the cycle removal +algorithm can find the other objects referenced by this object. Other +methods are available to define exotic object behaviors. The Class ID are globally allocated (i.e. for all runtimes). The JSClass are allocated per @code{JSRuntime}. @code{JS_SetClassProto()} diff --git a/libbf.c b/libbf.c index 3bf257a..fe1628e 100644 --- a/libbf.c +++ b/libbf.c @@ -1,7 +1,7 @@ /* * Tiny arbitrary precision floating point library * - * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2021 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/libbf.h b/libbf.h index 4576bf8..48e9d95 100644 --- a/libbf.h +++ b/libbf.h @@ -1,7 +1,7 @@ /* * Tiny arbitrary precision floating point library * - * Copyright (c) 2017-2020 Fabrice Bellard + * Copyright (c) 2017-2021 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/qjs.c b/qjs.c index 4dd11f8..d56b843 100644 --- a/qjs.c +++ b/qjs.c @@ -1,8 +1,8 @@ /* * QuickJS stand alone interpreter * - * Copyright (c) 2017-2020 Fabrice Bellard - * Copyright (c) 2017-2020 Charlie Gordon + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -156,7 +156,13 @@ static inline size_t js_trace_malloc_usable_size(void *ptr) #endif } -static void __attribute__((format(printf, 2, 3))) +static void +#ifdef _WIN32 +/* mingw printf is used */ +__attribute__((format(gnu_printf, 2, 3))) +#else +__attribute__((format(printf, 2, 3))) +#endif js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...) { va_list ap; diff --git a/qjsc.c b/qjsc.c index f5bda57..b9f1e4c 100644 --- a/qjsc.c +++ b/qjsc.c @@ -1,7 +1,7 @@ /* * QuickJS command line compiler * - * Copyright (c) 2018-2020 Fabrice Bellard + * Copyright (c) 2018-2021 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/qjscalc.js b/qjscalc.js index b7c5001..b1ad1e8 100644 --- a/qjscalc.js +++ b/qjscalc.js @@ -2299,8 +2299,13 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio function array_div(a, b) { return array_mul(a, b.inverse()); } - function array_scalar_div(a, b) { - return a * b.inverse(); + function array_element_wise_inverse(a) { + var r, i, n; + n = a.length; + r = []; + for(i = 0; i < n; i++) + r[i] = a[i].inverse(); + return r; } function array_eq(a, b) { var n, i; @@ -2337,14 +2342,14 @@ var Integer, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunctio right: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunction, Series], "*": array_scalar_mul, - "/": array_scalar_div, + "/"(a, b) { return a * b.inverse(); }, "**": generic_pow, /* XXX: only for integer */ }, { left: [Number, BigInt, Float, Fraction, Complex, Mod, Polynomial, PolyMod, RationalFunction, Series], "*"(a, b) { return array_scalar_mul(b, a); }, - "/"(a, b) { return array_scalar_div(b, a); }, + "/"(a, b) { return a * array_element_wise_inverse(b); }, }); add_props(Array.prototype, { diff --git a/quickjs-libc.c b/quickjs-libc.c index e8b81e9..e180dd0 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1,8 +1,8 @@ /* * QuickJS C library * - * Copyright (c) 2017-2020 Fabrice Bellard - * Copyright (c) 2017-2020 Charlie Gordon + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -1663,7 +1663,7 @@ static JSValue js_os_isatty(JSContext *ctx, JSValueConst this_val, int fd; if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; - return JS_NewBool(ctx, isatty(fd) == 1); + return JS_NewBool(ctx, (isatty(fd) != 0)); } #if defined(_WIN32) @@ -1689,6 +1689,10 @@ static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValueConst this_val, return obj; } +/* Windows 10 built-in VT100 emulation */ +#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 + static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1698,8 +1702,12 @@ static JSValue js_os_ttySetRaw(JSContext *ctx, JSValueConst this_val, if (JS_ToInt32(ctx, &fd, argv[0])) return JS_EXCEPTION; handle = (HANDLE)_get_osfhandle(fd); - - SetConsoleMode(handle, ENABLE_WINDOW_INPUT); + SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); + _setmode(fd, _O_BINARY); + if (fd == 0) { + handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ + SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } return JS_UNDEFINED; } #else @@ -1772,7 +1780,19 @@ static JSValue js_os_remove(JSContext *ctx, JSValueConst this_val, filename = JS_ToCString(ctx, argv[0]); if (!filename) return JS_EXCEPTION; - ret = js_get_errno(remove(filename)); +#if defined(_WIN32) + { + struct stat st; + if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) { + ret = rmdir(filename); + } else { + ret = unlink(filename); + } + } +#else + ret = remove(filename); +#endif + ret = js_get_errno(ret); JS_FreeCString(ctx, filename); return JS_NewInt32(ctx, ret); } @@ -2588,7 +2608,47 @@ static JSValue js_os_utimes(JSContext *ctx, JSValueConst this_val, return JS_NewInt32(ctx, ret); } -#if !defined(_WIN32) +/* sleep(delay_ms) */ +static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int64_t delay; + int ret; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + if (delay < 0) + delay = 0; +#if defined(_WIN32) + { + if (delay > INT32_MAX) + delay = INT32_MAX; + Sleep(delay); + ret = 0; + } +#else + { + struct timespec ts; + + ts.tv_sec = delay / 1000; + ts.tv_nsec = (delay % 1000) * 1000000; + ret = js_get_errno(nanosleep(&ts, NULL)); + } +#endif + return JS_NewInt32(ctx, ret); +} + +#if defined(_WIN32) +static char *realpath(const char *path, char *buf) +{ + if (!_fullpath(buf, path, PATH_MAX)) { + errno = ENOENT; + return NULL; + } else { + return buf; + } +} +#endif /* return [path, errorcode] */ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, @@ -2612,6 +2672,7 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, return make_string_error(ctx, buf, err); } +#if !defined(_WIN32) static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -3031,22 +3092,6 @@ static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, return JS_NewInt32(ctx, ret); } -/* sleep(delay_ms) */ -static JSValue js_os_sleep(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t delay; - struct timespec ts; - int ret; - - if (JS_ToInt64(ctx, &delay, argv[0])) - return JS_EXCEPTION; - ts.tv_sec = delay / 1000; - ts.tv_nsec = (delay % 1000) * 1000000; - ret = js_get_errno(nanosleep(&ts, NULL)); - return JS_NewInt32(ctx, ret); -} - /* dup(fd) */ static JSValue js_os_dup(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -3598,9 +3643,10 @@ static const JSCFunctionListEntry js_os_funcs[] = { #endif JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), JS_CFUNC_DEF("utimes", 3, js_os_utimes ), + JS_CFUNC_DEF("sleep", 1, js_os_sleep ), + JS_CFUNC_DEF("realpath", 1, js_os_realpath ), #if !defined(_WIN32) JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), - JS_CFUNC_DEF("realpath", 1, js_os_realpath ), JS_CFUNC_DEF("symlink", 2, js_os_symlink ), JS_CFUNC_DEF("readlink", 1, js_os_readlink ), JS_CFUNC_DEF("exec", 1, js_os_exec ), @@ -3608,7 +3654,6 @@ static const JSCFunctionListEntry js_os_funcs[] = { OS_FLAG(WNOHANG), JS_CFUNC_DEF("pipe", 0, js_os_pipe ), JS_CFUNC_DEF("kill", 2, js_os_kill ), - JS_CFUNC_DEF("sleep", 1, js_os_sleep ), JS_CFUNC_DEF("dup", 1, js_os_dup ), JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), #endif diff --git a/quickjs.c b/quickjs.c index a39ff8f..48aeffc 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1,8 +1,8 @@ /* * QuickJS Javascript Engine * - * Copyright (c) 2017-2020 Fabrice Bellard - * Copyright (c) 2017-2020 Charlie Gordon + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,8 @@ #include #elif defined(__linux__) #include +#elif defined(__FreeBSD__) +#include #endif #include "cutils.h" @@ -265,9 +267,10 @@ struct JSRuntime { struct list_head string_list; /* list of JSString.link */ #endif /* stack limitation */ - const uint8_t *stack_top; - size_t stack_size; /* in bytes */ - + uintptr_t stack_size; /* in bytes, 0 if no limit */ + uintptr_t stack_top; + uintptr_t stack_limit; /* lower stack limit */ + JSValue current_exception; /* true if inside an out of memory error, to avoid recursing */ BOOL in_out_of_memory : 8; @@ -1569,9 +1572,9 @@ static void set_dummy_numeric_ops(JSNumericOperations *ops) #if !defined(CONFIG_STACK_CHECK) /* no stack limitation */ -static inline uint8_t *js_get_stack_pointer(void) +static inline uintptr_t js_get_stack_pointer(void) { - return NULL; + return 0; } static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) @@ -1580,16 +1583,16 @@ static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) } #else /* Note: OS and CPU dependent */ -static inline uint8_t *js_get_stack_pointer(void) +static inline uintptr_t js_get_stack_pointer(void) { - return __builtin_frame_address(0); + return (uintptr_t)__builtin_frame_address(0); } static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) { - size_t size; - size = rt->stack_top - js_get_stack_pointer(); - return unlikely((size + alloca_size) > rt->stack_size); + uintptr_t sp; + sp = js_get_stack_pointer() - alloca_size; + return unlikely(sp < rt->stack_limit); } #endif @@ -1649,8 +1652,9 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) if (init_shape_hash(rt)) goto fail; - rt->stack_top = js_get_stack_pointer(); rt->stack_size = JS_DEFAULT_STACK_SIZE; + JS_UpdateStackTop(rt); + rt->current_exception = JS_NULL; return rt; @@ -2338,9 +2342,25 @@ JSRuntime *JS_GetRuntime(JSContext *ctx) return ctx->rt; } +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; + } +} + void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) { rt->stack_size = stack_size; + update_stack_limit(rt); +} + +void JS_UpdateStackTop(JSRuntime *rt) +{ + rt->stack_top = js_get_stack_pointer(); + update_stack_limit(rt); } static inline BOOL is_strict_mode(JSContext *ctx) @@ -3377,6 +3397,8 @@ static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, JSClass *cl, *new_class_array; struct list_head *el; + if (class_id >= (1 << 16)) + return -1; if (class_id < rt->class_count && rt->class_array[class_id].class_id != 0) return -1; @@ -8231,6 +8253,23 @@ static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, return TRUE; } +/* return -1 if exception */ +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) +{ + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) + return -1; + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + return 0; +} + /* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = TRUE and p->extensible = TRUE */ static int add_fast_array_element(JSContext *ctx, JSObject *p, @@ -8253,19 +8292,10 @@ static int add_fast_array_element(JSContext *ctx, JSObject *p, } } if (unlikely(new_len > p->u.array.u1.size)) { - uint32_t new_size; - size_t slack; - JSValue *new_array_prop; - /* XXX: potential arithmetic overflow */ - new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); - new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); - if (!new_array_prop) { + if (expand_fast_array(ctx, p, new_len)) { JS_FreeValue(ctx, val); return -1; } - new_size += slack / sizeof(*new_array_prop); - p->u.array.u.values = new_array_prop; - p->u.array.u1.size = new_size; } p->u.array.u.values[new_len - 1] = val; p->u.array.count = new_len; @@ -8279,22 +8309,24 @@ static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) JS_FreeValue(ctx, desc->value); } -/* generic (and slower) version of JS_SetProperty() for Reflect.set() */ +/* generic (and slower) version of JS_SetProperty() for + * Reflect.set(). 'obj' must be an object. */ static int JS_SetPropertyGeneric(JSContext *ctx, - JSObject *p, JSAtom prop, + JSValueConst obj, JSAtom prop, JSValue val, JSValueConst this_obj, int flags) { int ret; JSPropertyDescriptor desc; - - while (p != NULL) { + JSValue obj1; + JSObject *p; + + obj1 = JS_DupValue(ctx, obj); + for(;;) { + p = JS_VALUE_GET_OBJ(obj1); if (p->is_exotic) { const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; if (em && em->set_property) { - JSValue obj1; - /* set_property can free the prototype */ - obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); ret = em->set_property(ctx, obj1, prop, val, this_obj, flags); JS_FreeValue(ctx, obj1); @@ -8304,8 +8336,11 @@ static int JS_SetPropertyGeneric(JSContext *ctx, } ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (ret < 0) + if (ret < 0) { + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); return ret; + } if (ret) { if (desc.flags & JS_PROP_GETSET) { JSObject *setter; @@ -8316,27 +8351,38 @@ static int JS_SetPropertyGeneric(JSContext *ctx, ret = call_setter(ctx, setter, this_obj, val, flags); JS_FreeValue(ctx, desc.getter); JS_FreeValue(ctx, desc.setter); + JS_FreeValue(ctx, obj1); return ret; } else { JS_FreeValue(ctx, desc.value); if (!(desc.flags & JS_PROP_WRITABLE)) { + JS_FreeValue(ctx, obj1); goto read_only_error; } } break; } - p = p->shape->proto; + /* Note: at this point 'obj1' cannot be a proxy. XXX: may have + to check recursion */ + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; } + JS_FreeValue(ctx, obj1); - if (!JS_IsObject(this_obj)) + if (!JS_IsObject(this_obj)) { + JS_FreeValue(ctx, val); return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object"); - + } + p = JS_VALUE_GET_OBJ(this_obj); /* modify the property in this_obj if it already exists */ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); - if (ret < 0) + if (ret < 0) { + JS_FreeValue(ctx, val); return ret; + } if (ret) { if (desc.flags & JS_PROP_GETSET) { JS_FreeValue(ctx, desc.getter); @@ -15022,10 +15068,10 @@ static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst * static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) { - JSObject *p, *p1; + JSObject *p; JSPropertyEnum *tab_atom; int i; - JSValue enum_obj; + JSValue enum_obj, obj1; JSForInIterator *it; uint32_t tag, tab_atom_count; @@ -15054,20 +15100,34 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) return enum_obj; - p = JS_VALUE_GET_OBJ(obj); - /* fast path: assume no enumerable properties in the prototype chain */ - p1 = p->shape->proto; - while (p1 != NULL) { - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p1, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + obj1 = JS_DupValue(ctx, obj); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) goto fail; + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + JS_FreeValue(ctx, obj1); + goto fail; + } js_free_prop_enum(ctx, tab_atom, tab_atom_count); if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); goto slow_path; } - p1 = p1->shape->proto; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } } + + p = JS_VALUE_GET_OBJ(obj); + if (p->fast_array) { JSShape *sh; JSShapeProperty *prs; @@ -15095,17 +15155,30 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) slow_path: /* non enumerable properties hide the enumerables ones in the prototype chain */ - while (p != NULL) { - if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, - JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) + obj1 = JS_DupValue(ctx, obj); + for(;;) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, obj1); goto fail; + } for(i = 0; i < tab_atom_count; i++) { JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, (tab_atom[i].is_enumerable ? JS_PROP_ENUMERABLE : 0)); } js_free_prop_enum(ctx, tab_atom, tab_atom_count); - p = p->shape->proto; + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } } return enum_obj; @@ -15572,8 +15645,10 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, uint32_t i, tab_atom_count; JSObject *p; JSObject *pexcl = NULL; - int ret = 0, flags; - + int ret, gpn_flags; + JSPropertyDescriptor desc; + BOOL is_enumerable; + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) return 0; @@ -15581,37 +15656,57 @@ static __exception int JS_CopyDataProperties(JSContext *ctx, pexcl = JS_VALUE_GET_OBJ(excluded); p = JS_VALUE_GET_OBJ(source); + + gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it + introduces a visible change */ + if (em && em->get_own_property_names) { + gpn_flags &= ~JS_GPN_ENUM_ONLY; + } + } if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | - JS_GPN_ENUM_ONLY)) + gpn_flags)) return -1; - - flags = JS_PROP_C_W_E; - + for (i = 0; i < tab_atom_count; i++) { if (pexcl) { ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); if (ret) { if (ret < 0) - break; - ret = 0; + goto exception; continue; } } - ret = -1; + if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { + /* test if the property is enumerable */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); + if (ret < 0) + goto exception; + if (!ret) + continue; + is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; + js_free_desc(ctx, &desc); + if (!is_enumerable) + continue; + } val = JS_GetProperty(ctx, source, tab_atom[i].atom); if (JS_IsException(val)) - break; + goto exception; if (setprop) ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); else - ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, flags); + ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, + JS_PROP_C_W_E); if (ret < 0) - break; - ret = 0; + goto exception; } js_free_prop_enum(ctx, tab_atom, tab_atom_count); - return ret; + return 0; + exception: + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return -1; } /* only valid inside C functions */ @@ -16654,7 +16749,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_check_ctor): if (JS_IsUndefined(new_target)) { - JS_ThrowTypeError(caller_ctx, "class constructors must be invoked with 'new'"); + JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); goto exception; } BREAK; @@ -17770,8 +17865,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = JS_ValueToAtom(ctx, sp[-2]); if (unlikely(atom == JS_ATOM_NULL)) goto exception; - ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(sp[-3]), - atom, sp[-1], sp[-4], + ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4], JS_PROP_THROW_STRICT); JS_FreeAtom(ctx, atom); JS_FreeValue(ctx, sp[-4]); @@ -22678,7 +22772,7 @@ static __exception int js_parse_left_hand_side_expr(JSParseState *s) return js_parse_postfix_expr(s, PF_POSTFIX_CALL); } -/* XXX: is there is nicer solution ? */ +/* XXX: could generate specific bytecode */ static __exception int js_parse_class_default_ctor(JSParseState *s, BOOL has_super, JSFunctionDef **pfd) @@ -22691,7 +22785,8 @@ static __exception int js_parse_class_default_ctor(JSParseState *s, js_parse_get_pos(s, &pos); if (has_super) { - str = "(...a){super(...a);}"; + /* spec change: no argument evaluation */ + str = "(){super(...arguments);}"; func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; } else { str = "(){}"; @@ -34227,6 +34322,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) bc_put_leb128(s, b->cpool_count); bc_put_leb128(s, b->byte_code_len); if (b->vardefs) { + /* XXX: this field is redundant */ bc_put_leb128(s, b->arg_count + b->var_count); for(i = 0; i < b->arg_count + b->var_count; i++) { JSVarDef *vd = &b->vardefs[i]; @@ -35196,6 +35292,16 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); b->header.ref_count = 1; + if (local_count != 0) { + b->vardefs = (void *)((uint8_t*)b + vardefs_offset); + } + if (b->closure_var_count != 0) { + b->closure_var = (void *)((uint8_t*)b + closure_var_offset); + } + if (b->cpool_count != 0) { + b->cpool = (void *)((uint8_t*)b + cpool_offset); + } + add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); @@ -35211,7 +35317,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) if (local_count != 0) { bc_read_trace(s, "vars {\n"); - b->vardefs = (void *)((uint8_t*)b + vardefs_offset); for(i = 0; i < local_count; i++) { JSVarDef *vd = &b->vardefs[i]; if (bc_get_atom(s, &vd->var_name)) @@ -35236,7 +35341,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) } if (b->closure_var_count != 0) { bc_read_trace(s, "closure vars {\n"); - b->closure_var = (void *)((uint8_t*)b + closure_var_offset); for(i = 0; i < b->closure_var_count; i++) { JSClosureVar *cv = &b->closure_var[i]; int var_idx; @@ -35288,7 +35392,6 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) } if (b->cpool_count != 0) { bc_read_trace(s, "cpool {\n"); - b->cpool = (void *)((uint8_t*)b + cpool_offset); for(i = 0; i < b->cpool_count; i++) { JSValue val; val = JS_ReadObjectRec(s); @@ -37493,6 +37596,10 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, } if (js_get_length32(ctx, &len, array_arg)) return NULL; + if (len > JS_MAX_LOCAL_VARS) { + JS_ThrowInternalError(ctx, "too many arguments"); + return NULL; + } /* avoid allocating 0 bytes */ tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); if (!tab) @@ -38293,9 +38400,16 @@ static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, n = 0; for(k = 0; k < len; k++) { - present = JS_TryGetPropertyInt64(ctx, obj, k, &val); - if (present < 0) - goto exception; + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k, &val); + if (present < 0) + goto exception; + } if (present) { index_val = JS_NewInt64(ctx, k); if (JS_IsException(index_val)) @@ -38413,18 +38527,32 @@ static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val, } k1 = (special & special_reduceRight) ? len - k - 1 : k; k++; - present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); - if (present < 0) - goto exception; - if (present) + if (special & special_TA) { + acc = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(acc)) + goto exception; break; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); + if (present < 0) + goto exception; + if (present) + break; + } } } for (; k < len; k++) { k1 = (special & special_reduceRight) ? len - k - 1 : k; - present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); - if (present < 0) - goto exception; + if (special & special_TA) { + val = JS_GetPropertyInt64(ctx, obj, k1); + if (JS_IsException(val)) + goto exception; + present = TRUE; + } else { + present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); + if (present < 0) + goto exception; + } if (present) { index_val = JS_NewInt64(ctx, k1); if (JS_IsException(index_val)) @@ -38829,27 +38957,64 @@ static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, int64_t len, from, newLen; obj = JS_ToObject(ctx, this_val); - if (js_get_length64(ctx, &len, obj)) - goto exception; - newLen = len + argc; - if (newLen > MAX_SAFE_INTEGER) { - JS_ThrowTypeError(ctx, "Array loo long"); - goto exception; - } - from = len; - if (unshift && argc > 0) { - if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) - goto exception; - from = 0; - } - for(i = 0; i < argc; i++) { - if (JS_SetPropertyInt64(ctx, obj, from + i, - JS_DupValue(ctx, argv[i])) < 0) - goto exception; - } - if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) - goto exception; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ARRAY || + !p->fast_array || !p->extensible) + goto generic_case; + /* length must be writable */ + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) + goto generic_case; + /* check the length */ + if (unlikely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT)) + goto generic_case; + len = JS_VALUE_GET_INT(p->prop[0].u.value); + /* we don't support holes */ + if (unlikely(len != p->u.array.count)) + goto generic_case; + newLen = len + argc; + if (unlikely(newLen > INT32_MAX)) + goto generic_case; + if (newLen > p->u.array.u1.size) { + if (expand_fast_array(ctx, p, newLen)) + goto exception; + } + if (unshift && argc > 0) { + memmove(p->u.array.u.values + argc, p->u.array.u.values, + len * sizeof(p->u.array.u.values[0])); + from = 0; + } else { + from = len; + } + for(i = 0; i < argc; i++) { + p->u.array.u.values[from + i] = JS_DupValue(ctx, argv[i]); + } + p->u.array.count = newLen; + p->prop[0].u.value = JS_NewInt32(ctx, newLen); + } else { + generic_case: + if (js_get_length64(ctx, &len, obj)) + goto exception; + newLen = len + argc; + if (newLen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "Array loo long"); + goto exception; + } + from = len; + if (unshift && argc > 0) { + if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) + goto exception; + from = 0; + } + for(i = 0; i < argc; i++) { + if (JS_SetPropertyInt64(ctx, obj, from + i, + JS_DupValue(ctx, argv[i])) < 0) + goto exception; + } + if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) + goto exception; + } JS_FreeValue(ctx, obj); return JS_NewInt64(ctx, newLen); @@ -39079,6 +39244,11 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, int64_t sourceIndex, elementLen; int present, is_array; + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); if (present < 0) @@ -41816,7 +41986,7 @@ static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, } /* OS dependent. d = argv[0] is in ms from 1970. Return the difference - between local time and UTC time 'd' in minutes */ + between UTC time and local time 'd' in minutes */ static int getTimezoneOffset(int64_t time) { #if defined(_WIN32) /* XXX: TODO */ @@ -44165,7 +44335,7 @@ static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, atom = JS_ValueToAtom(ctx, prop); if (unlikely(atom == JS_ATOM_NULL)) return JS_EXCEPTION; - ret = JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(obj), atom, + ret = JS_SetPropertyGeneric(ctx, obj, atom, JS_DupValue(ctx, val), receiver, 0); JS_FreeAtom(ctx, atom); if (ret < 0) @@ -44513,7 +44683,7 @@ static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, if (!s) return -1; if (JS_IsUndefined(method)) { - return JS_SetPropertyGeneric(ctx, JS_VALUE_GET_OBJ(s->target), atom, + return JS_SetPropertyGeneric(ctx, s->target, atom, JS_DupValue(ctx, value), receiver, flags); } @@ -48134,24 +48304,22 @@ static void string_skip_non_spaces(JSString *sp, int *pp) { *pp += 1; } -/* parse a numeric field */ -static int string_get_field(JSString *sp, int *pp, int64_t *pval) { +/* parse a numeric field with an optional sign if accept_sign is TRUE */ +static int string_get_digits(JSString *sp, int *pp, int64_t *pval) { int64_t v = 0; - int c, p = *pp; - - /* skip non digits, should only skip spaces? */ - while (p < sp->len) { - c = string_get(sp, p); - if (c >= '0' && c <= '9') - break; - p++; - } + int c, p = *pp, p_start; + if (p >= sp->len) return -1; + p_start = p; while (p < sp->len) { c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) - break; + if (!(c >= '0' && c <= '9')) { + if (p == p_start) + return -1; + else + break; + } v = v * 10 + c - '0'; p++; } @@ -48160,8 +48328,25 @@ static int string_get_field(JSString *sp, int *pp, int64_t *pval) { return 0; } +static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) { + int res, sgn, p = *pp; + + if (p >= sp->len) + return -1; + + sgn = string_get(sp, p); + if (sgn == '-' || sgn == '+') + p++; + + res = string_get_digits(sp, &p, pval); + if (res == 0 && sgn == '-') + *pval = -*pval; + *pp = p; + return res; +} + /* parse a fixed width numeric field */ -static int string_get_digits(JSString *sp, int *pp, int n, int64_t *pval) { +static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) { int64_t v = 0; int i, c, p = *pp; @@ -48179,23 +48364,32 @@ static int string_get_digits(JSString *sp, int *pp, int n, int64_t *pval) { return 0; } -/* parse a signed numeric field */ -static int string_get_signed_field(JSString *sp, int *pp, int64_t *pval) { - int sgn, res; - - if (*pp >= sp->len) +static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) { + /* parse milliseconds as a fractional part, round to nearest */ + /* XXX: the spec does not indicate which rounding should be used */ + int mul = 1000, ms = 0, p = *pp, c, p_start; + if (p >= sp->len) return -1; - - sgn = string_get(sp, *pp); - if (sgn == '-' || sgn == '+') - *pp += 1; - - res = string_get_field(sp, pp, pval); - if (res == 0 && sgn == '-') - *pval = -*pval; - return res; + p_start = p; + while (p < sp->len) { + c = string_get(sp, p); + if (!(c >= '0' && c <= '9')) { + if (p == p_start) + return -1; + else + break; + } + if (mul == 1 && c >= '5') + ms += 1; + ms += (c - '0') * (mul /= 10); + p++; + } + *pval = ms; + *pp = p; + return 0; } + static int find_abbrev(JSString *sp, int p, const char *list, int count) { int n, i; @@ -48234,7 +48428,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, double fields1[7]; int64_t tz, hh, mm; double d; - int p, i, c, sgn; + int p, i, c, sgn, l; JSString *sp; BOOL is_local; @@ -48249,50 +48443,74 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { /* ISO format */ /* year field can be negative */ - /* XXX: could be stricter */ - if (string_get_signed_field(sp, &p, &fields[0])) + if (string_get_signed_digits(sp, &p, &fields[0])) goto done; - is_local = TRUE; - for (i = 1; i < 6; i++) { - if (string_get_field(sp, &p, &fields[i])) + for (i = 1; i < 7; i++) { + if (p >= sp->len) + break; + switch(i) { + case 1: + case 2: + c = '-'; + break; + case 3: + c = 'T'; + break; + case 4: + case 5: + c = ':'; + break; + case 6: + c = '.'; break; - } - if (i <= 3) { - /* no time: UTC by default */ - is_local = FALSE; - } else if (i == 6 && p < sp->len && string_get(sp, p) == '.') { - /* parse milliseconds as a fractional part, round to nearest */ - /* XXX: the spec does not indicate which rounding should be used */ - int mul = 1000, ms = 0; - while (++p < sp->len) { - int c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) - break; - if (mul == 1 && c >= '5') - ms += 1; - ms += (c - '0') * (mul /= 10); } - fields[6] = ms; + if (string_get(sp, p) != c) + break; + p++; + if (i == 6) { + if (string_get_milliseconds(sp, &p, &fields[i])) + goto done; + } else { + if (string_get_digits(sp, &p, &fields[i])) + goto done; + } } + /* no time: UTC by default */ + is_local = (i > 3); fields[1] -= 1; - /* parse the time zone offset if present: [+-]HH:mm */ + /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ tz = 0; if (p < sp->len) { sgn = string_get(sp, p); if (sgn == '+' || sgn == '-') { - if (string_get_field(sp, &p, &hh)) + p++; + l = sp->len - p; + if (l != 4 && l != 5) goto done; - if (string_get_field(sp, &p, &mm)) + if (string_get_fixed_width_digits(sp, &p, 2, &hh)) + goto done; + if (l == 5) { + if (string_get(sp, p) != ':') + goto done; + p++; + } + if (string_get_fixed_width_digits(sp, &p, 2, &mm)) goto done; tz = hh * 60 + mm; if (sgn == '-') tz = -tz; is_local = FALSE; } else if (sgn == 'Z') { + p++; is_local = FALSE; + } else { + goto done; } + /* error if extraneous characters */ + if (p != sp->len) + goto done; } } else { /* toString or toUTCString format */ @@ -48304,7 +48522,7 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, c = string_get(sp, p); if (c >= '0' && c <= '9') { /* day of month first */ - if (string_get_field(sp, &p, &fields[2])) + if (string_get_digits(sp, &p, &fields[2])) goto done; if (string_get_month(sp, &p, &fields[1])) goto done; @@ -48312,16 +48530,26 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, /* month first */ if (string_get_month(sp, &p, &fields[1])) goto done; - if (string_get_field(sp, &p, &fields[2])) + string_skip_spaces(sp, &p); + if (string_get_digits(sp, &p, &fields[2])) goto done; } + /* year */ string_skip_spaces(sp, &p); - if (string_get_signed_field(sp, &p, &fields[0])) + if (string_get_signed_digits(sp, &p, &fields[0])) goto done; /* hour, min, seconds */ + string_skip_spaces(sp, &p); for(i = 0; i < 3; i++) { - if (string_get_field(sp, &p, &fields[3 + i])) + if (i == 1 || i == 2) { + if (p >= sp->len) + goto done; + if (string_get(sp, p) != ':') + goto done; + p++; + } + if (string_get_digits(sp, &p, &fields[3 + i])) goto done; } // XXX: parse optional milliseconds? @@ -48333,9 +48561,9 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, sgn = string_get(sp, p); if (sgn == '+' || sgn == '-') { p++; - if (string_get_digits(sp, &p, 2, &hh)) + if (string_get_fixed_width_digits(sp, &p, 2, &hh)) goto done; - if (string_get_digits(sp, &p, 2, &mm)) + if (string_get_fixed_width_digits(sp, &p, 2, &mm)) goto done; tz = hh * 60 + mm; if (sgn == '-') @@ -51886,10 +52114,16 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, inc = 1; } - if (validate_typed_array(ctx, this_val)) - goto exception; p = JS_VALUE_GET_OBJ(this_val); - + /* if the array was detached, no need to go further (but no + exception is raised) */ + if (typed_array_is_detached(ctx, p)) { + /* "includes" scans all the properties, so "undefined" can match */ + if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) + res = 0; + goto done; + } + is_bigint = 0; is_int = 0; /* avoid warning */ v64 = 0; /* avoid warning */ @@ -51925,7 +52159,6 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, goto done; } - p = JS_VALUE_GET_OBJ(this_val); switch (p->class_id) { case JS_CLASS_INT8_ARRAY: if (is_int && (int8_t)v64 == v64) @@ -52114,13 +52347,16 @@ static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val, } } el = JS_GetPropertyUint32(ctx, this_val, i); - if (JS_IsException(el)) - goto fail; - if (toLocaleString) { - el = JS_ToLocaleStringFree(ctx, el); + /* Can return undefined for example if the typed array is detached */ + if (!JS_IsNull(el) && !JS_IsUndefined(el)) { + if (JS_IsException(el)) + goto fail; + if (toLocaleString) { + el = JS_ToLocaleStringFree(ctx, el); + } + if (string_buffer_concat_value_free(b, el)) + goto fail; } - if (string_buffer_concat_value_free(b, el)) - goto fail; } JS_FreeValue(ctx, sep); return string_buffer_end(b); diff --git a/quickjs.h b/quickjs.h index 92457a2..d4a5cd3 100644 --- a/quickjs.h +++ b/quickjs.h @@ -1,8 +1,8 @@ /* * QuickJS Javascript Engine * - * Copyright (c) 2017-2020 Fabrice Bellard - * Copyright (c) 2017-2020 Charlie Gordon + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -333,7 +333,11 @@ JSRuntime *JS_NewRuntime(void); void JS_SetRuntimeInfo(JSRuntime *rt, const char *info); void JS_SetMemoryLimit(JSRuntime *rt, size_t limit); void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold); +/* use 0 to disable maximum stack size check */ void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size); +/* should be called when changing thread to update the stack top value + used to check stack overflow. */ +void JS_UpdateStackTop(JSRuntime *rt); JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque); void JS_FreeRuntime(JSRuntime *rt); void *JS_GetRuntimeOpaque(JSRuntime *rt); diff --git a/release.sh b/release.sh index 6dc89fb..26fba1b 100755 --- a/release.sh +++ b/release.sh @@ -131,7 +131,8 @@ outdir="/tmp/${d}" rm -rf $outdir mkdir -p $outdir $outdir/doc $outdir/tests $outdir/examples -cp Makefile VERSION TODO Changelog readme.txt release.sh unicode_download.sh \ +cp Makefile VERSION TODO Changelog readme.txt LICENSE \ + release.sh unicode_download.sh \ qjs.c qjsc.c qjscalc.js repl.js \ quickjs.c quickjs.h quickjs-atom.h \ quickjs-libc.c quickjs-libc.h quickjs-opcode.h \ diff --git a/run-test262.c b/run-test262.c index c860dbb..2092cac 100644 --- a/run-test262.c +++ b/run-test262.c @@ -1,8 +1,8 @@ /* * ECMA Test 262 Runner for QuickJS * - * Copyright (c) 2017-2020 Fabrice Bellard - * Copyright (c) 2017-2020 Charlie Gordon + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/test262.conf b/test262.conf index f30a4ac..6fbb579 100644 --- a/test262.conf +++ b/test262.conf @@ -51,10 +51,10 @@ testdir=test262/test AggregateError align-detached-buffer-semantics-with-web-reality arbitrary-module-namespace-names=skip +Array.prototype.at=skip Array.prototype.flat Array.prototype.flatMap Array.prototype.flatten -Array.prototype.item=skip Array.prototype.values ArrayBuffer arrow-function @@ -141,7 +141,7 @@ string-trimming String.fromCodePoint String.prototype.endsWith String.prototype.includes -String.prototype.item=skip +String.prototype.at=skip String.prototype.matchAll String.prototype.replaceAll String.prototype.trimEnd @@ -166,7 +166,7 @@ tail-call-optimization=skip template top-level-await=skip TypedArray -TypedArray.prototype.item=skip +TypedArray.prototype.at=skip u180e Uint16Array Uint32Array diff --git a/test262_errors.txt b/test262_errors.txt index 502a650..b7f6aef 100644 --- a/test262_errors.txt +++ b/test262_errors.txt @@ -2,50 +2,34 @@ test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-r test262/test/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js:20: strict mode: Test262Error: Expected a ReferenceError but got a ReferenceError test262/test/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js:46: SyntaxError: invalid group name test262/test/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js:46: strict mode: SyntaxError: invalid group name -test262/test/built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/every/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/filter/BigInt/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js:36: Test262Error: throws a TypeError getting a value from the detached buffer Expected a TypeError to be thrown but no exception was thrown at all (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/findIndex/BigInt/predicate-may-detach-buffer.js:36: strict mode: Test262Error: throws a TypeError getting a value from the detached buffer Expected a TypeError to be thrown but no exception was thrown at all (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/forEach/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/map/BigInt/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/map/BigInt/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js:20: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js:20: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/reduce/BigInt/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/reduceRight/BigInt/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js:29: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js:29: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/some/BigInt/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js:28: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js:28: strict mode: Test262Error: Expected SameValue(«1», «2») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:37: Test262Error: (Testing with BigInt64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:37: strict mode: Test262Error: (Testing with BigInt64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:38: Test262Error: (Testing with Float64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:38: strict mode: Test262Error: (Testing with Float64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/BigInt/index-prop-desc.js:21: Test262Error: Expected SameValue(«43», «42») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/BigInt/index-prop-desc.js:21: strict mode: Test262Error: Expected SameValue(«43», «42») to be true (Testing with BigInt64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/index-prop-desc.js:22: Test262Error: Expected SameValue(«43», «42») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/GetOwnProperty/index-prop-desc.js:22: strict mode: Test262Error: Expected SameValue(«43», «42») to be true (Testing with Float64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer-realm.js:36: strict mode: TypeError: out-of-bound numeric index (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:46: Test262Error: (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/BigInt/detached-buffer.js:46: strict mode: Test262Error: (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:47: Test262Error: (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/DefineOwnProperty/detached-buffer.js:47: strict mode: Test262Error: (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer-realm.js:37: strict mode: TypeError: out-of-bound numeric index (Testing with BigInt64Array.) test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:34: TypeError: cannot convert bigint to number (Testing with BigInt64Array.) test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/detached-buffer.js:32: strict mode: TypeError: out-of-bound numeric index (Testing with BigInt64Array.) -test262/test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer-realm.js:36: strict mode: TypeError: out-of-bound numeric index (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-minus-zero.js:20: Test262Error: Reflect.set("new TA([42n])", "-0", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-minus-zero.js:20: strict mode: Test262Error: Reflect.set("new TA([42n])", "-0", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-not-integer.js:21: Test262Error: Reflect.set("new TA([42n])", "1.1", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-not-integer.js:21: strict mode: Test262Error: Reflect.set("new TA([42n])", "1.1", 1n) must return true Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-out-of-bounds.js:27: Test262Error: Reflect.set("new TA([42n])", "-1", 1n) must return false Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/key-is-out-of-bounds.js:27: strict mode: Test262Error: Reflect.set("new TA([42n])", "-1", 1n) must return false Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/tonumber-value-detached-buffer.js:24: Test262Error: Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/BigInt/tonumber-value-detached-buffer.js:24: strict mode: Test262Error: Expected SameValue(«false», «true») to be true (Testing with BigInt64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer-realm.js:37: strict mode: TypeError: out-of-bound numeric index (Testing with Float64Array.) test262/test/built-ins/TypedArrayConstructors/internals/Set/detached-buffer.js:32: strict mode: TypeError: out-of-bound numeric index (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-minus-zero.js:22: Test262Error: Reflect.set(sample, "-0", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-minus-zero.js:22: strict mode: Test262Error: Reflect.set(sample, "-0", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-not-integer.js:22: Test262Error: Reflect.set(sample, "1.1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-not-integer.js:22: strict mode: Test262Error: Reflect.set(sample, "1.1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js:22: Test262Error: Reflect.set(sample, "-1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js:22: strict mode: Test262Error: Reflect.set(sample, "-1", 1) must return true Expected SameValue(«false», «true») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js:39: Test262Error: Expected SameValue(«false», «true») to be true (Testing with Float64Array.) +test262/test/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-detached-buffer.js:39: strict mode: Test262Error: Expected SameValue(«false», «true») to be true (Testing with Float64Array.) test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: cannot read property 'c' of undefined test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: cannot read property '_b' of undefined +test262/test/language/statements/for-of/head-lhs-async-invalid.js:14: unexpected error type: Test262: This statement should not be evaluated. +test262/test/language/statements/for-of/head-lhs-async-invalid.js:14: strict mode: unexpected error type: Test262: This statement should not be evaluated. diff --git a/tests/test_builtin.js b/tests/test_builtin.js index c044b2c..c5c0f83 100644 --- a/tests/test_builtin.js +++ b/tests/test_builtin.js @@ -399,7 +399,7 @@ function test_eval() function test_typed_array() { - var buffer, a, i; + var buffer, a, i, str; a = new Uint8Array(4); assert(a.length, 4); @@ -438,8 +438,13 @@ function test_typed_array() a[0] = 1; a = new Uint8Array(buffer); - - assert(a.toString(), "0,0,255,255,0,0,0,0,0,0,128,63,255,255,255,255"); + + str = a.toString(); + /* test little and big endian cases */ + if (str !== "0,0,255,255,0,0,0,0,0,0,128,63,255,255,255,255" && + str !== "0,0,255,255,0,0,0,0,63,128,0,0,255,255,255,255") { + assert(false); + } assert(a.buffer, buffer);