2021-03-27 release

pull/60/head
bellard 2 years ago
parent 204682fb87
commit b5e62895c6

@ -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

@ -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.

@ -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

@ -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

@ -1 +1 @@
2020-11-08
2021-03-27

@ -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()}

@ -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

@ -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

12
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;

@ -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

@ -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, {

@ -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

@ -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 <malloc/malloc.h>
#elif defined(__linux__)
#include <malloc.h>
#elif defined(__FreeBSD__)
#include <malloc_np.h>
#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))
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;
from = 0;
}
for(i = 0; i < argc; i++) {
if (JS_SetPropertyInt64(ctx, obj, from + i,
JS_DupValue(ctx, argv[i])) < 0)
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_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;
}
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);
switch(i) {
case 1:
case 2:
c = '-';
break;
case 3:
c = 'T';
break;
case 4:
case 5:
c = ':';
break;
case 6:
c = '.';
break;
}
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;
}
fields[6] = ms;
}
/* 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_fixed_width_digits(sp, &p, 2, &hh))
goto done;
if (string_get_field(sp, &p, &mm))
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,