mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
1952 lines
63 KiB
C
1952 lines
63 KiB
C
/*
|
|
* QuickJS Javascript Engine
|
|
*
|
|
* Copyright (c) 2017-2021 Fabrice Bellard
|
|
* Copyright (c) 2017-2021 Charlie Gordon
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
#include "third_party/quickjs/internal.h"
|
|
|
|
asm(".ident\t\"\\n\\n\
|
|
QuickJS (MIT License)\\n\
|
|
Copyright (c) 2017-2021 Fabrice Bellard\\n\
|
|
Copyright (c) 2017-2021 Charlie Gordon\"");
|
|
asm(".include \"libc/disclaimer.inc\"");
|
|
/* clang-format off */
|
|
|
|
/* Check if an object has a generalized numeric property. Return value:
|
|
-1 for exception,
|
|
TRUE if property exists, stored into *pval,
|
|
FALSE if proprty does not exist.
|
|
*/
|
|
static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
|
|
{
|
|
JSValue val = JS_UNDEFINED;
|
|
JSAtom prop;
|
|
int present;
|
|
if (LIKELY((uint64_t)idx <= JS_ATOM_MAX_INT)) {
|
|
/* fast path */
|
|
present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
|
|
if (present > 0) {
|
|
val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
present = -1;
|
|
}
|
|
} else {
|
|
prop = JS_NewAtomInt64(ctx, idx);
|
|
present = -1;
|
|
if (LIKELY(prop != JS_ATOM_NULL)) {
|
|
present = JS_HasProperty(ctx, obj, prop);
|
|
if (present > 0) {
|
|
val = JS_GetProperty(ctx, obj, prop);
|
|
if (UNLIKELY(JS_IsException(val)))
|
|
present = -1;
|
|
}
|
|
JS_FreeAtom(ctx, prop);
|
|
}
|
|
}
|
|
*pval = val;
|
|
return present;
|
|
}
|
|
|
|
static int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
|
|
{
|
|
JSAtom prop;
|
|
int res;
|
|
if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
|
|
/* fast path for fast arrays */
|
|
return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
|
|
}
|
|
prop = JS_NewAtomInt64(ctx, idx);
|
|
if (prop == JS_ATOM_NULL)
|
|
return -1;
|
|
res = JS_DeleteProperty(ctx, obj, prop, flags);
|
|
JS_FreeAtom(ctx, prop);
|
|
return res;
|
|
}
|
|
|
|
static int JS_CopySubArray(JSContext *ctx,
|
|
JSValueConst obj, int64_t to_pos,
|
|
int64_t from_pos, int64_t count, int dir)
|
|
{
|
|
int64_t i, from, to;
|
|
JSValue val;
|
|
int fromPresent;
|
|
/* XXX: should special case fast arrays */
|
|
for (i = 0; i < count; i++) {
|
|
if (dir < 0) {
|
|
from = from_pos + count - i - 1;
|
|
to = to_pos + count - i - 1;
|
|
} else {
|
|
from = from_pos + i;
|
|
to = to_pos + i;
|
|
}
|
|
fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
|
|
if (fromPresent < 0)
|
|
goto exception;
|
|
if (fromPresent) {
|
|
if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
|
|
goto exception;
|
|
} else {
|
|
if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
return 0;
|
|
exception:
|
|
return -1;
|
|
}
|
|
|
|
JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj;
|
|
int i;
|
|
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
|
|
if (JS_IsException(obj))
|
|
return obj;
|
|
if (argc == 1 && JS_IsNumber(argv[0])) {
|
|
uint32_t len;
|
|
if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
|
|
goto fail;
|
|
if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
|
|
goto fail;
|
|
} else {
|
|
for(i = 0; i < argc; i++) {
|
|
if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
|
|
goto fail;
|
|
}
|
|
}
|
|
return obj;
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
// from(items, mapfn = void 0, this_arg = void 0)
|
|
JSValueConst items = argv[0], mapfn, this_arg;
|
|
JSValueConst args[2];
|
|
JSValue stack[2];
|
|
JSValue iter, r, v, v2, arrayLike;
|
|
int64_t k, len;
|
|
int done, mapping;
|
|
mapping = FALSE;
|
|
mapfn = JS_UNDEFINED;
|
|
this_arg = JS_UNDEFINED;
|
|
r = JS_UNDEFINED;
|
|
arrayLike = JS_UNDEFINED;
|
|
stack[0] = JS_UNDEFINED;
|
|
stack[1] = JS_UNDEFINED;
|
|
if (argc > 1) {
|
|
mapfn = argv[1];
|
|
if (!JS_IsUndefined(mapfn)) {
|
|
if (check_function(ctx, mapfn))
|
|
goto exception;
|
|
mapping = 1;
|
|
if (argc > 2)
|
|
this_arg = argv[2];
|
|
}
|
|
}
|
|
iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
|
|
if (JS_IsException(iter))
|
|
goto exception;
|
|
if (!JS_IsUndefined(iter)) {
|
|
JS_FreeValue(ctx, iter);
|
|
if (JS_IsConstructor(ctx, this_val))
|
|
r = JS_CallConstructor(ctx, this_val, 0, NULL);
|
|
else
|
|
r = JS_NewArray(ctx);
|
|
if (JS_IsException(r))
|
|
goto exception;
|
|
stack[0] = JS_DupValue(ctx, items);
|
|
if (js_for_of_start(ctx, &stack[1], FALSE))
|
|
goto exception;
|
|
for (k = 0;; k++) {
|
|
v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
|
|
if (JS_IsException(v))
|
|
goto exception_close;
|
|
if (done)
|
|
break;
|
|
if (mapping) {
|
|
args[0] = v;
|
|
args[1] = JS_NewInt32(ctx, k);
|
|
v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
|
|
JS_FreeValue(ctx, v);
|
|
v = v2;
|
|
if (JS_IsException(v))
|
|
goto exception_close;
|
|
}
|
|
if (JS_DefinePropertyValueInt64(ctx, r, k, v,
|
|
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
|
|
goto exception_close;
|
|
}
|
|
} else {
|
|
arrayLike = JS_ToObject(ctx, items);
|
|
if (JS_IsException(arrayLike))
|
|
goto exception;
|
|
if (js_get_length64(ctx, &len, arrayLike) < 0)
|
|
goto exception;
|
|
v = JS_NewInt64(ctx, len);
|
|
args[0] = v;
|
|
if (JS_IsConstructor(ctx, this_val)) {
|
|
r = JS_CallConstructor(ctx, this_val, 1, args);
|
|
} else {
|
|
r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
|
|
}
|
|
JS_FreeValue(ctx, v);
|
|
if (JS_IsException(r))
|
|
goto exception;
|
|
for(k = 0; k < len; k++) {
|
|
v = JS_GetPropertyInt64(ctx, arrayLike, k);
|
|
if (JS_IsException(v))
|
|
goto exception;
|
|
if (mapping) {
|
|
args[0] = v;
|
|
args[1] = JS_NewInt32(ctx, k);
|
|
v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
|
|
JS_FreeValue(ctx, v);
|
|
v = v2;
|
|
if (JS_IsException(v))
|
|
goto exception;
|
|
}
|
|
if (JS_DefinePropertyValueInt64(ctx, r, k, v,
|
|
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
|
|
goto exception;
|
|
goto done;
|
|
exception_close:
|
|
if (!JS_IsUndefined(stack[0]))
|
|
JS_IteratorClose(ctx, stack[0], TRUE);
|
|
exception:
|
|
JS_FreeValue(ctx, r);
|
|
r = JS_EXCEPTION;
|
|
done:
|
|
JS_FreeValue(ctx, arrayLike);
|
|
JS_FreeValue(ctx, stack[0]);
|
|
JS_FreeValue(ctx, stack[1]);
|
|
return r;
|
|
}
|
|
|
|
static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj, args[1];
|
|
int i;
|
|
if (JS_IsConstructor(ctx, this_val)) {
|
|
args[0] = JS_NewInt32(ctx, argc);
|
|
obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
|
|
} else {
|
|
obj = JS_NewArray(ctx);
|
|
}
|
|
if (JS_IsException(obj))
|
|
return JS_EXCEPTION;
|
|
for(i = 0; i < argc; i++) {
|
|
if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
|
|
JS_PROP_THROW) < 0) {
|
|
goto fail;
|
|
}
|
|
}
|
|
if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
int ret;
|
|
ret = JS_IsArray(ctx, argv[0]);
|
|
if (ret < 0)
|
|
return JS_EXCEPTION;
|
|
else
|
|
return JS_NewBool(ctx, ret);
|
|
}
|
|
|
|
static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
|
|
JSValueConst len_val)
|
|
{
|
|
JSValue ctor, ret, species;
|
|
int res;
|
|
JSContext *realm;
|
|
res = JS_IsArray(ctx, obj);
|
|
if (res < 0)
|
|
return JS_EXCEPTION;
|
|
if (!res)
|
|
return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
|
|
ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
|
|
if (JS_IsException(ctor))
|
|
return ctor;
|
|
if (JS_IsConstructor(ctx, ctor)) {
|
|
/* legacy web compatibility */
|
|
realm = JS_GetFunctionRealm(ctx, ctor);
|
|
if (!realm) {
|
|
JS_FreeValue(ctx, ctor);
|
|
return JS_EXCEPTION;
|
|
}
|
|
if (realm != ctx &&
|
|
js_same_value(ctx, ctor, realm->array_ctor)) {
|
|
JS_FreeValue(ctx, ctor);
|
|
ctor = JS_UNDEFINED;
|
|
}
|
|
}
|
|
if (JS_IsObject(ctor)) {
|
|
species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
|
|
JS_FreeValue(ctx, ctor);
|
|
if (JS_IsException(species))
|
|
return species;
|
|
ctor = species;
|
|
if (JS_IsNull(ctor))
|
|
ctor = JS_UNDEFINED;
|
|
}
|
|
if (JS_IsUndefined(ctor)) {
|
|
return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
|
|
} else {
|
|
ret = JS_CallConstructor(ctx, ctor, 1, &len_val);
|
|
JS_FreeValue(ctx, ctor);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_array_funcs[] = {
|
|
JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
|
|
JS_CFUNC_DEF("from", 1, js_array_from ),
|
|
JS_CFUNC_DEF("of", 0, js_array_of ),
|
|
JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
|
|
};
|
|
|
|
static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
|
|
{
|
|
JSValue val;
|
|
if (!JS_IsObject(obj))
|
|
return FALSE;
|
|
val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
|
|
if (JS_IsException(val))
|
|
return -1;
|
|
if (!JS_IsUndefined(val))
|
|
return JS_ToBoolFree(ctx, val);
|
|
return JS_IsArray(ctx, obj);
|
|
}
|
|
|
|
static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj, arr, val;
|
|
JSValueConst e;
|
|
int64_t len, k, n;
|
|
int i, res;
|
|
arr = JS_UNDEFINED;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (JS_IsException(obj))
|
|
goto exception;
|
|
arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
|
|
if (JS_IsException(arr))
|
|
goto exception;
|
|
n = 0;
|
|
for (i = -1; i < argc; i++) {
|
|
if (i < 0)
|
|
e = obj;
|
|
else
|
|
e = argv[i];
|
|
res = JS_isConcatSpreadable(ctx, e);
|
|
if (res < 0)
|
|
goto exception;
|
|
if (res) {
|
|
if (js_get_length64(ctx, &len, e))
|
|
goto exception;
|
|
if (n + len > MAX_SAFE_INTEGER) {
|
|
JS_ThrowTypeError(ctx, "Array loo long");
|
|
goto exception;
|
|
}
|
|
for (k = 0; k < len; k++, n++) {
|
|
res = JS_TryGetPropertyInt64(ctx, e, k, &val);
|
|
if (res < 0)
|
|
goto exception;
|
|
if (res) {
|
|
if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
|
|
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
} else {
|
|
if (n >= MAX_SAFE_INTEGER) {
|
|
JS_ThrowTypeError(ctx, "Array loo long");
|
|
goto exception;
|
|
}
|
|
if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
|
|
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
n++;
|
|
}
|
|
}
|
|
if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
|
|
goto exception;
|
|
JS_FreeValue(ctx, obj);
|
|
return arr;
|
|
exception:
|
|
JS_FreeValue(ctx, arr);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int special)
|
|
{
|
|
JSValue obj, val, index_val, res, ret;
|
|
JSValueConst args[3];
|
|
JSValueConst func, this_arg;
|
|
int64_t len, k, n;
|
|
int present;
|
|
ret = JS_UNDEFINED;
|
|
val = JS_UNDEFINED;
|
|
if (special & special_TA) {
|
|
obj = JS_DupValue(ctx, this_val);
|
|
len = js_typed_array_get_length_internal(ctx, obj);
|
|
if (len < 0)
|
|
goto exception;
|
|
} else {
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
}
|
|
func = argv[0];
|
|
this_arg = JS_UNDEFINED;
|
|
if (argc > 1)
|
|
this_arg = argv[1];
|
|
if (check_function(ctx, func))
|
|
goto exception;
|
|
switch (special) {
|
|
case special_every:
|
|
case special_every | special_TA:
|
|
ret = JS_TRUE;
|
|
break;
|
|
case special_some:
|
|
case special_some | special_TA:
|
|
ret = JS_FALSE;
|
|
break;
|
|
case special_map:
|
|
/* XXX: JS_ArraySpeciesCreate should take int64_t */
|
|
ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));
|
|
if (JS_IsException(ret))
|
|
goto exception;
|
|
break;
|
|
case special_filter:
|
|
ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
|
|
if (JS_IsException(ret))
|
|
goto exception;
|
|
break;
|
|
case special_map | special_TA:
|
|
args[0] = obj;
|
|
args[1] = JS_NewInt32(ctx, len);
|
|
ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
|
|
if (JS_IsException(ret))
|
|
goto exception;
|
|
break;
|
|
case special_filter | special_TA:
|
|
ret = JS_NewArray(ctx);
|
|
if (JS_IsException(ret))
|
|
goto exception;
|
|
break;
|
|
}
|
|
n = 0;
|
|
for(k = 0; k < len; k++) {
|
|
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))
|
|
goto exception;
|
|
args[0] = val;
|
|
args[1] = index_val;
|
|
args[2] = obj;
|
|
res = JS_Call(ctx, func, this_arg, 3, args);
|
|
JS_FreeValue(ctx, index_val);
|
|
if (JS_IsException(res))
|
|
goto exception;
|
|
switch (special) {
|
|
case special_every:
|
|
case special_every | special_TA:
|
|
if (!JS_ToBoolFree(ctx, res)) {
|
|
ret = JS_FALSE;
|
|
goto done;
|
|
}
|
|
break;
|
|
case special_some:
|
|
case special_some | special_TA:
|
|
if (JS_ToBoolFree(ctx, res)) {
|
|
ret = JS_TRUE;
|
|
goto done;
|
|
}
|
|
break;
|
|
case special_map:
|
|
if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
|
|
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
break;
|
|
case special_map | special_TA:
|
|
if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
break;
|
|
case special_filter:
|
|
case special_filter | special_TA:
|
|
if (JS_ToBoolFree(ctx, res)) {
|
|
if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
|
|
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
break;
|
|
default:
|
|
JS_FreeValue(ctx, res);
|
|
break;
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
val = JS_UNDEFINED;
|
|
}
|
|
}
|
|
done:
|
|
if (special == (special_filter | special_TA)) {
|
|
JSValue arr;
|
|
args[0] = obj;
|
|
args[1] = JS_NewInt32(ctx, n);
|
|
arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
|
|
if (JS_IsException(arr))
|
|
goto exception;
|
|
args[0] = ret;
|
|
res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
|
|
if (check_exception_free(ctx, res))
|
|
goto exception;
|
|
JS_FreeValue(ctx, ret);
|
|
ret = arr;
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, obj);
|
|
return ret;
|
|
exception:
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int special)
|
|
{
|
|
JSValue obj, val, index_val, acc, acc1;
|
|
JSValueConst args[4];
|
|
JSValueConst func;
|
|
int64_t len, k, k1;
|
|
int present;
|
|
acc = JS_UNDEFINED;
|
|
val = JS_UNDEFINED;
|
|
if (special & special_TA) {
|
|
obj = JS_DupValue(ctx, this_val);
|
|
len = js_typed_array_get_length_internal(ctx, obj);
|
|
if (len < 0)
|
|
goto exception;
|
|
} else {
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
}
|
|
func = argv[0];
|
|
if (check_function(ctx, func))
|
|
goto exception;
|
|
k = 0;
|
|
if (argc > 1) {
|
|
acc = JS_DupValue(ctx, argv[1]);
|
|
} else {
|
|
for(;;) {
|
|
if (k >= len) {
|
|
JS_ThrowTypeError(ctx, "empty array");
|
|
goto exception;
|
|
}
|
|
k1 = (special & special_reduceRight) ? len - k - 1 : k;
|
|
k++;
|
|
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;
|
|
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))
|
|
goto exception;
|
|
args[0] = acc;
|
|
args[1] = val;
|
|
args[2] = index_val;
|
|
args[3] = obj;
|
|
acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
|
|
JS_FreeValue(ctx, index_val);
|
|
JS_FreeValue(ctx, val);
|
|
val = JS_UNDEFINED;
|
|
if (JS_IsException(acc1))
|
|
goto exception;
|
|
JS_FreeValue(ctx, acc);
|
|
acc = acc1;
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, obj);
|
|
return acc;
|
|
exception:
|
|
JS_FreeValue(ctx, acc);
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj;
|
|
int64_t len, start, end;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
start = 0;
|
|
if (argc > 1 && !JS_IsUndefined(argv[1])) {
|
|
if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
|
|
goto exception;
|
|
}
|
|
end = len;
|
|
if (argc > 2 && !JS_IsUndefined(argv[2])) {
|
|
if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
|
|
goto exception;
|
|
}
|
|
/* XXX: should special case fast arrays */
|
|
while (start < end) {
|
|
if (JS_SetPropertyInt64(ctx, obj, start,
|
|
JS_DupValue(ctx, argv[0])) < 0)
|
|
goto exception;
|
|
start++;
|
|
}
|
|
return obj;
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
/* Access an Array's internal JSValue array if available */
|
|
BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
|
|
JSValue **arrpp, uint32_t *countp)
|
|
{
|
|
/* Try and handle fast arrays explicitly */
|
|
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) {
|
|
*countp = p->u.array.count;
|
|
*arrpp = p->u.array.u.values;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj, val;
|
|
int64_t len, n, res;
|
|
JSValue *arrp;
|
|
uint32_t count;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
res = FALSE;
|
|
if (len > 0) {
|
|
n = 0;
|
|
if (argc > 1) {
|
|
if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
|
|
goto exception;
|
|
}
|
|
if (js_get_fast_array(ctx, obj, &arrp, &count)) {
|
|
for (; n < count; n++) {
|
|
if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
|
|
JS_DupValue(ctx, arrp[n]),
|
|
JS_EQ_SAME_VALUE_ZERO)) {
|
|
res = TRUE;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
for (; n < len; n++) {
|
|
val = JS_GetPropertyInt64(ctx, obj, n);
|
|
if (JS_IsException(val))
|
|
goto exception;
|
|
if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,
|
|
JS_EQ_SAME_VALUE_ZERO)) {
|
|
res = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
done:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_NewBool(ctx, res);
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj, val;
|
|
int64_t len, n, res;
|
|
JSValue *arrp;
|
|
uint32_t count;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
res = -1;
|
|
if (len > 0) {
|
|
n = 0;
|
|
if (argc > 1) {
|
|
if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
|
|
goto exception;
|
|
}
|
|
if (js_get_fast_array(ctx, obj, &arrp, &count)) {
|
|
for (; n < count; n++) {
|
|
if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
|
|
JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {
|
|
res = n;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
for (; n < len; n++) {
|
|
int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
|
|
if (present < 0)
|
|
goto exception;
|
|
if (present) {
|
|
if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
|
|
res = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
done:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_NewInt64(ctx, res);
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj, val;
|
|
int64_t len, n, res;
|
|
int present;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
res = -1;
|
|
if (len > 0) {
|
|
n = len - 1;
|
|
if (argc > 1) {
|
|
if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
|
|
goto exception;
|
|
}
|
|
/* XXX: should special case fast arrays */
|
|
for (; n >= 0; n--) {
|
|
present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
|
|
if (present < 0)
|
|
goto exception;
|
|
if (present) {
|
|
if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
|
|
res = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_NewInt64(ctx, res);
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int findIndex)
|
|
{
|
|
JSValueConst func, this_arg;
|
|
JSValueConst args[3];
|
|
JSValue obj, val, index_val, res;
|
|
int64_t len, k;
|
|
index_val = JS_UNDEFINED;
|
|
val = JS_UNDEFINED;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
func = argv[0];
|
|
if (check_function(ctx, func))
|
|
goto exception;
|
|
this_arg = JS_UNDEFINED;
|
|
if (argc > 1)
|
|
this_arg = argv[1];
|
|
for(k = 0; k < len; k++) {
|
|
index_val = JS_NewInt64(ctx, k);
|
|
if (JS_IsException(index_val))
|
|
goto exception;
|
|
val = JS_GetPropertyValue(ctx, obj, index_val);
|
|
if (JS_IsException(val))
|
|
goto exception;
|
|
args[0] = val;
|
|
args[1] = index_val;
|
|
args[2] = this_val;
|
|
res = JS_Call(ctx, func, this_arg, 3, args);
|
|
if (JS_IsException(res))
|
|
goto exception;
|
|
if (JS_ToBoolFree(ctx, res)) {
|
|
if (findIndex) {
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, obj);
|
|
return index_val;
|
|
} else {
|
|
JS_FreeValue(ctx, index_val);
|
|
JS_FreeValue(ctx, obj);
|
|
return val;
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, index_val);
|
|
}
|
|
JS_FreeValue(ctx, obj);
|
|
if (findIndex)
|
|
return JS_NewInt32(ctx, -1);
|
|
else
|
|
return JS_UNDEFINED;
|
|
exception:
|
|
JS_FreeValue(ctx, index_val);
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj, method, ret;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (JS_IsException(obj))
|
|
return JS_EXCEPTION;
|
|
method = JS_GetProperty(ctx, obj, JS_ATOM_join);
|
|
if (JS_IsException(method)) {
|
|
ret = JS_EXCEPTION;
|
|
} else
|
|
if (!JS_IsFunction(ctx, method)) {
|
|
/* Use intrinsic Object.prototype.toString */
|
|
JS_FreeValue(ctx, method);
|
|
ret = js_object_toString(ctx, obj, 0, NULL);
|
|
} else {
|
|
ret = JS_CallFree(ctx, method, obj, 0, NULL);
|
|
}
|
|
JS_FreeValue(ctx, obj);
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int toLocaleString)
|
|
{
|
|
JSValue obj, sep = JS_UNDEFINED, el;
|
|
StringBuffer b_s, *b = &b_s;
|
|
JSString *p = NULL;
|
|
int64_t i, n;
|
|
int c;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &n, obj))
|
|
goto exception;
|
|
c = ','; /* default separator */
|
|
if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
|
|
sep = JS_ToString(ctx, argv[0]);
|
|
if (JS_IsException(sep))
|
|
goto exception;
|
|
p = JS_VALUE_GET_STRING(sep);
|
|
if (p->len == 1 && !p->is_wide_char)
|
|
c = p->u.str8[0];
|
|
else
|
|
c = -1;
|
|
}
|
|
string_buffer_init(ctx, b, 0);
|
|
for(i = 0; i < n; i++) {
|
|
if (i > 0) {
|
|
if (c >= 0) {
|
|
string_buffer_putc8(b, c);
|
|
} else {
|
|
string_buffer_concat(b, p, 0, p->len);
|
|
}
|
|
}
|
|
el = JS_GetPropertyUint32(ctx, obj, i);
|
|
if (JS_IsException(el))
|
|
goto fail;
|
|
if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
|
|
if (toLocaleString) {
|
|
el = JS_ToLocaleStringFree(ctx, el);
|
|
}
|
|
if (string_buffer_concat_value_free(b, el))
|
|
goto fail;
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, sep);
|
|
JS_FreeValue(ctx, obj);
|
|
return string_buffer_end(b);
|
|
fail:
|
|
string_buffer_free(b);
|
|
JS_FreeValue(ctx, sep);
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int shift)
|
|
{
|
|
JSValue obj, res = JS_UNDEFINED;
|
|
int64_t len, newLen;
|
|
JSValue *arrp;
|
|
uint32_t count32;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
newLen = 0;
|
|
if (len > 0) {
|
|
newLen = len - 1;
|
|
/* Special case fast arrays */
|
|
if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
|
|
JSObject *p = JS_VALUE_GET_OBJ(obj);
|
|
if (shift) {
|
|
res = arrp[0];
|
|
memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
|
|
p->u.array.count--;
|
|
} else {
|
|
res = arrp[count32 - 1];
|
|
p->u.array.count--;
|
|
}
|
|
} else {
|
|
if (shift) {
|
|
res = JS_GetPropertyInt64(ctx, obj, 0);
|
|
if (JS_IsException(res))
|
|
goto exception;
|
|
if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
|
|
goto exception;
|
|
} else {
|
|
res = JS_GetPropertyInt64(ctx, obj, newLen);
|
|
if (JS_IsException(res))
|
|
goto exception;
|
|
}
|
|
if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
|
|
goto exception;
|
|
JS_FreeValue(ctx, obj);
|
|
return res;
|
|
exception:
|
|
JS_FreeValue(ctx, res);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
/* 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 */
|
|
int add_fast_array_element(JSContext *ctx, JSObject *p, JSValue val, int flags)
|
|
{
|
|
uint32_t new_len, array_len;
|
|
/* extend the array by one */
|
|
/* XXX: convert to slow array if new_len > 2^31-1 elements */
|
|
new_len = p->u.array.count + 1;
|
|
/* update the length if necessary. We assume that if the length is
|
|
not an integer, then if it >= 2^31. */
|
|
if (LIKELY(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
|
|
array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
|
|
if (new_len > array_len) {
|
|
if (UNLIKELY(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
|
|
JS_FreeValue(ctx, val);
|
|
return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
|
|
}
|
|
p->prop[0].u.value = JS_NewInt32(ctx, new_len);
|
|
}
|
|
}
|
|
if (UNLIKELY(new_len > p->u.array.u1.size)) {
|
|
if (expand_fast_array(ctx, p, new_len)) {
|
|
JS_FreeValue(ctx, val);
|
|
return -1;
|
|
}
|
|
}
|
|
p->u.array.u.values[new_len - 1] = val;
|
|
p->u.array.count = new_len;
|
|
return TRUE;
|
|
}
|
|
|
|
JSValue js_array_push(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int unshift)
|
|
{
|
|
JSValue obj;
|
|
int i;
|
|
int64_t len, from, newLen;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
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);
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj, lval, hval;
|
|
JSValue *arrp;
|
|
int64_t len, l, h;
|
|
int l_present, h_present;
|
|
uint32_t count32;
|
|
lval = JS_UNDEFINED;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
/* Special case fast arrays */
|
|
if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
|
|
uint32_t ll, hh;
|
|
if (count32 > 1) {
|
|
for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
|
|
lval = arrp[ll];
|
|
arrp[ll] = arrp[hh];
|
|
arrp[hh] = lval;
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
for (l = 0, h = len - 1; l < h; l++, h--) {
|
|
l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
|
|
if (l_present < 0)
|
|
goto exception;
|
|
h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
|
|
if (h_present < 0)
|
|
goto exception;
|
|
if (h_present) {
|
|
if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
|
|
goto exception;
|
|
if (l_present) {
|
|
if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
|
|
lval = JS_UNDEFINED;
|
|
goto exception;
|
|
}
|
|
lval = JS_UNDEFINED;
|
|
} else {
|
|
if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
} else {
|
|
if (l_present) {
|
|
if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
|
|
lval = JS_UNDEFINED;
|
|
goto exception;
|
|
}
|
|
lval = JS_UNDEFINED;
|
|
}
|
|
}
|
|
}
|
|
return obj;
|
|
exception:
|
|
JS_FreeValue(ctx, lval);
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
|
|
{
|
|
/* Try and handle fast arrays explicitly */
|
|
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) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int splice)
|
|
{
|
|
JSValue obj, arr, val, len_val;
|
|
int64_t len, start, k, final, n, count, del_count, new_len;
|
|
int kPresent;
|
|
JSValue *arrp;
|
|
uint32_t count32, i, item_count;
|
|
arr = JS_UNDEFINED;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
|
|
goto exception;
|
|
if (splice) {
|
|
if (argc == 0) {
|
|
item_count = 0;
|
|
del_count = 0;
|
|
} else
|
|
if (argc == 1) {
|
|
item_count = 0;
|
|
del_count = len - start;
|
|
} else {
|
|
item_count = argc - 2;
|
|
if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
|
|
goto exception;
|
|
}
|
|
if (len + item_count - del_count > MAX_SAFE_INTEGER) {
|
|
JS_ThrowTypeError(ctx, "Array loo long");
|
|
goto exception;
|
|
}
|
|
count = del_count;
|
|
} else {
|
|
item_count = 0; /* avoid warning */
|
|
final = len;
|
|
if (!JS_IsUndefined(argv[1])) {
|
|
if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
|
|
goto exception;
|
|
}
|
|
count = max_int64(final - start, 0);
|
|
}
|
|
len_val = JS_NewInt64(ctx, count);
|
|
arr = JS_ArraySpeciesCreate(ctx, obj, len_val);
|
|
JS_FreeValue(ctx, len_val);
|
|
if (JS_IsException(arr))
|
|
goto exception;
|
|
k = start;
|
|
final = start + count;
|
|
n = 0;
|
|
/* The fast array test on arr ensures that
|
|
JS_CreateDataPropertyUint32() won't modify obj in case arr is
|
|
an exotic object */
|
|
/* Special case fast arrays */
|
|
if (js_get_fast_array(ctx, obj, &arrp, &count32) &&
|
|
js_is_fast_array(ctx, arr)) {
|
|
/* XXX: should share code with fast array constructor */
|
|
for (; k < final && k < count32; k++, n++) {
|
|
if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
/* Copy the remaining elements if any (handle case of inherited properties) */
|
|
for (; k < final; k++, n++) {
|
|
kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
|
|
if (kPresent < 0)
|
|
goto exception;
|
|
if (kPresent) {
|
|
if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
|
|
goto exception;
|
|
if (splice) {
|
|
new_len = len + item_count - del_count;
|
|
if (item_count != del_count) {
|
|
if (JS_CopySubArray(ctx, obj, start + item_count,
|
|
start + del_count, len - (start + del_count),
|
|
item_count <= del_count ? +1 : -1) < 0)
|
|
goto exception;
|
|
for (k = len; k-- > new_len; ) {
|
|
if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
for (i = 0; i < item_count; i++) {
|
|
if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
|
|
goto exception;
|
|
}
|
|
if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
|
|
goto exception;
|
|
}
|
|
JS_FreeValue(ctx, obj);
|
|
return arr;
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
JS_FreeValue(ctx, arr);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
JSValue obj;
|
|
int64_t len, from, to, final, count;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
|
|
goto exception;
|
|
if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
|
|
goto exception;
|
|
final = len;
|
|
if (argc > 2 && !JS_IsUndefined(argv[2])) {
|
|
if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
|
|
goto exception;
|
|
}
|
|
count = min_int64(final - from, len - to);
|
|
if (JS_CopySubArray(ctx, obj, to, from, count,
|
|
(from < to && to < from + count) ? -1 : +1))
|
|
goto exception;
|
|
return obj;
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
|
|
JSValueConst source, int64_t sourceLen,
|
|
int64_t targetIndex, int depth,
|
|
JSValueConst mapperFunction,
|
|
JSValueConst thisArg)
|
|
{
|
|
JSValue element;
|
|
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)
|
|
return -1;
|
|
if (!present)
|
|
continue;
|
|
if (!JS_IsUndefined(mapperFunction)) {
|
|
JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
|
|
element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
|
|
JS_FreeValue(ctx, (JSValue)args[0]);
|
|
JS_FreeValue(ctx, (JSValue)args[1]);
|
|
if (JS_IsException(element))
|
|
return -1;
|
|
}
|
|
if (depth > 0) {
|
|
is_array = JS_IsArray(ctx, element);
|
|
if (is_array < 0)
|
|
goto fail;
|
|
if (is_array) {
|
|
if (js_get_length64(ctx, &elementLen, element) < 0)
|
|
goto fail;
|
|
targetIndex = JS_FlattenIntoArray(ctx, target, element,
|
|
elementLen, targetIndex,
|
|
depth - 1,
|
|
JS_UNDEFINED, JS_UNDEFINED);
|
|
if (targetIndex < 0)
|
|
goto fail;
|
|
JS_FreeValue(ctx, element);
|
|
continue;
|
|
}
|
|
}
|
|
if (targetIndex >= MAX_SAFE_INTEGER) {
|
|
JS_ThrowTypeError(ctx, "Array too long");
|
|
goto fail;
|
|
}
|
|
if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
|
|
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
|
|
return -1;
|
|
targetIndex++;
|
|
}
|
|
return targetIndex;
|
|
fail:
|
|
JS_FreeValue(ctx, element);
|
|
return -1;
|
|
}
|
|
|
|
static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int map)
|
|
{
|
|
JSValue obj, arr;
|
|
JSValueConst mapperFunction, thisArg;
|
|
int64_t sourceLen;
|
|
int depthNum;
|
|
arr = JS_UNDEFINED;
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &sourceLen, obj))
|
|
goto exception;
|
|
depthNum = 1;
|
|
mapperFunction = JS_UNDEFINED;
|
|
thisArg = JS_UNDEFINED;
|
|
if (map) {
|
|
mapperFunction = argv[0];
|
|
if (argc > 1) {
|
|
thisArg = argv[1];
|
|
}
|
|
if (check_function(ctx, mapperFunction))
|
|
goto exception;
|
|
} else {
|
|
if (argc > 0 && !JS_IsUndefined(argv[0])) {
|
|
if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
|
|
goto exception;
|
|
}
|
|
}
|
|
arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
|
|
if (JS_IsException(arr))
|
|
goto exception;
|
|
if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
|
|
mapperFunction, thisArg) < 0)
|
|
goto exception;
|
|
JS_FreeValue(ctx, obj);
|
|
return arr;
|
|
exception:
|
|
JS_FreeValue(ctx, obj);
|
|
JS_FreeValue(ctx, arr);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
typedef struct ValueSlot {
|
|
JSValue val;
|
|
JSString *str;
|
|
int64_t pos;
|
|
} ValueSlot;
|
|
|
|
struct array_sort_context {
|
|
JSContext *ctx;
|
|
int exception;
|
|
int has_method;
|
|
JSValueConst method;
|
|
};
|
|
|
|
static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
|
|
struct array_sort_context *psc = opaque;
|
|
JSContext *ctx = psc->ctx;
|
|
JSValueConst argv[2];
|
|
JSValue res;
|
|
ValueSlot *ap = (ValueSlot *)(void *)a;
|
|
ValueSlot *bp = (ValueSlot *)(void *)b;
|
|
int cmp;
|
|
if (psc->exception)
|
|
return 0;
|
|
if (psc->has_method) {
|
|
/* custom sort function is specified as returning 0 for identical
|
|
* objects: avoid method call overhead.
|
|
*/
|
|
if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
|
|
goto cmp_same;
|
|
argv[0] = ap->val;
|
|
argv[1] = bp->val;
|
|
res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
|
|
if (JS_IsException(res))
|
|
goto exception;
|
|
if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
|
|
int val = JS_VALUE_GET_INT(res);
|
|
cmp = (val > 0) - (val < 0);
|
|
} else {
|
|
double val;
|
|
if (JS_ToFloat64Free(ctx, &val, res) < 0)
|
|
goto exception;
|
|
cmp = (val > 0) - (val < 0);
|
|
}
|
|
} else {
|
|
/* Not supposed to bypass ToString even for identical objects as
|
|
* tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
|
|
*/
|
|
if (!ap->str) {
|
|
JSValue str = JS_ToString(ctx, ap->val);
|
|
if (JS_IsException(str))
|
|
goto exception;
|
|
ap->str = JS_VALUE_GET_STRING(str);
|
|
}
|
|
if (!bp->str) {
|
|
JSValue str = JS_ToString(ctx, bp->val);
|
|
if (JS_IsException(str))
|
|
goto exception;
|
|
bp->str = JS_VALUE_GET_STRING(str);
|
|
}
|
|
cmp = js_string_compare(ctx, ap->str, bp->str);
|
|
}
|
|
if (cmp != 0)
|
|
return cmp;
|
|
cmp_same:
|
|
/* make sort stable: compare array offsets */
|
|
return (ap->pos > bp->pos) - (ap->pos < bp->pos);
|
|
exception:
|
|
psc->exception = 1;
|
|
return 0;
|
|
}
|
|
|
|
static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
struct array_sort_context asc = { ctx, 0, 0, argv[0] };
|
|
JSValue obj = JS_UNDEFINED;
|
|
ValueSlot *array = NULL;
|
|
size_t array_size = 0, pos = 0, n = 0;
|
|
int64_t i, len, undefined_count = 0;
|
|
int present;
|
|
if (!JS_IsUndefined(asc.method)) {
|
|
if (check_function(ctx, asc.method))
|
|
goto exception;
|
|
asc.has_method = 1;
|
|
}
|
|
obj = JS_ToObject(ctx, this_val);
|
|
if (js_get_length64(ctx, &len, obj))
|
|
goto exception;
|
|
/* XXX: should special case fast arrays */
|
|
for (i = 0; i < len; i++) {
|
|
if (pos >= array_size) {
|
|
size_t new_size, slack;
|
|
ValueSlot *new_array;
|
|
new_size = (array_size + (array_size >> 1) + 31) & ~15;
|
|
new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
|
|
if (new_array == NULL)
|
|
goto exception;
|
|
new_size += slack / sizeof(*new_array);
|
|
array = new_array;
|
|
array_size = new_size;
|
|
}
|
|
present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
|
|
if (present < 0)
|
|
goto exception;
|
|
if (present == 0)
|
|
continue;
|
|
if (JS_IsUndefined(array[pos].val)) {
|
|
undefined_count++;
|
|
continue;
|
|
}
|
|
array[pos].str = NULL;
|
|
array[pos].pos = i;
|
|
pos++;
|
|
}
|
|
rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
|
|
if (asc.exception)
|
|
goto exception;
|
|
/* XXX: should special case fast arrays */
|
|
while (n < pos) {
|
|
if (array[n].str)
|
|
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
|
|
if (array[n].pos == n) {
|
|
JS_FreeValue(ctx, array[n].val);
|
|
} else {
|
|
if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
|
|
n++;
|
|
goto exception;
|
|
}
|
|
}
|
|
n++;
|
|
}
|
|
js_free(ctx, array);
|
|
for (i = n; undefined_count-- > 0; i++) {
|
|
if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
|
|
goto fail;
|
|
}
|
|
for (; i < len; i++) {
|
|
if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
|
|
goto fail;
|
|
}
|
|
return obj;
|
|
exception:
|
|
for (; n < pos; n++) {
|
|
JS_FreeValue(ctx, array[n].val);
|
|
if (array[n].str)
|
|
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
|
|
}
|
|
js_free(ctx, array);
|
|
fail:
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
|
JSArrayIteratorData *it = p->u.array_iterator_data;
|
|
if (it) {
|
|
JS_FreeValueRT(rt, it->obj);
|
|
js_free_rt(rt, it);
|
|
}
|
|
}
|
|
|
|
void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
|
JSArrayIteratorData *it = p->u.array_iterator_data;
|
|
if (it) {
|
|
JS_MarkValue(rt, it->obj, mark_func);
|
|
}
|
|
}
|
|
|
|
JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
|
|
{
|
|
JSValue obj;
|
|
int i;
|
|
obj = JS_NewArray(ctx);
|
|
if (JS_IsException(obj))
|
|
return JS_EXCEPTION;
|
|
for(i = 0; i < len; i++) {
|
|
if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {
|
|
JS_FreeValue(ctx, obj);
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv, int magic)
|
|
{
|
|
JSValue enum_obj, arr;
|
|
JSArrayIteratorData *it;
|
|
JSIteratorKindEnum kind;
|
|
int class_id;
|
|
|
|
kind = magic & 3;
|
|
if (magic & 4) {
|
|
/* string iterator case */
|
|
arr = JS_ToStringCheckObject(ctx, this_val);
|
|
class_id = JS_CLASS_STRING_ITERATOR;
|
|
} else {
|
|
arr = JS_ToObject(ctx, this_val);
|
|
class_id = JS_CLASS_ARRAY_ITERATOR;
|
|
}
|
|
if (JS_IsException(arr))
|
|
goto fail;
|
|
enum_obj = JS_NewObjectClass(ctx, class_id);
|
|
if (JS_IsException(enum_obj))
|
|
goto fail;
|
|
it = js_malloc(ctx, sizeof(*it));
|
|
if (!it)
|
|
goto fail1;
|
|
it->obj = arr;
|
|
it->kind = kind;
|
|
it->idx = 0;
|
|
JS_SetOpaque(enum_obj, it);
|
|
return enum_obj;
|
|
fail1:
|
|
JS_FreeValue(ctx, enum_obj);
|
|
fail:
|
|
JS_FreeValue(ctx, arr);
|
|
return JS_EXCEPTION;
|
|
}
|
|
|
|
JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
BOOL *pdone, int magic)
|
|
{
|
|
JSArrayIteratorData *it;
|
|
uint32_t len, idx;
|
|
JSValue val, obj;
|
|
JSObject *p;
|
|
it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
|
|
if (!it)
|
|
goto fail1;
|
|
if (JS_IsUndefined(it->obj))
|
|
goto done;
|
|
p = JS_VALUE_GET_OBJ(it->obj);
|
|
if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
|
|
p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
|
|
if (typed_array_is_detached(ctx, p)) {
|
|
JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
|
|
goto fail1;
|
|
}
|
|
len = p->u.array.count;
|
|
} else {
|
|
if (js_get_length32(ctx, &len, it->obj)) {
|
|
fail1:
|
|
*pdone = FALSE;
|
|
return JS_EXCEPTION;
|
|
}
|
|
}
|
|
idx = it->idx;
|
|
if (idx >= len) {
|
|
JS_FreeValue(ctx, it->obj);
|
|
it->obj = JS_UNDEFINED;
|
|
done:
|
|
*pdone = TRUE;
|
|
return JS_UNDEFINED;
|
|
}
|
|
it->idx = idx + 1;
|
|
*pdone = FALSE;
|
|
if (it->kind == JS_ITERATOR_KIND_KEY) {
|
|
return JS_NewUint32(ctx, idx);
|
|
} else {
|
|
val = JS_GetPropertyUint32(ctx, it->obj, idx);
|
|
if (JS_IsException(val))
|
|
return JS_EXCEPTION;
|
|
if (it->kind == JS_ITERATOR_KIND_VALUE) {
|
|
return val;
|
|
} else {
|
|
JSValueConst args[2];
|
|
JSValue num;
|
|
num = JS_NewUint32(ctx, idx);
|
|
args[0] = num;
|
|
args[1] = val;
|
|
obj = js_create_array(ctx, 2, args);
|
|
JS_FreeValue(ctx, val);
|
|
JS_FreeValue(ctx, num);
|
|
return obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_array_proto_funcs[] = {
|
|
JS_CFUNC_DEF("concat", 1, js_array_concat ),
|
|
JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
|
|
JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
|
|
JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
|
|
JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
|
|
JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
|
|
JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
|
|
JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
|
|
JS_CFUNC_DEF("fill", 1, js_array_fill ),
|
|
JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),
|
|
JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
|
|
JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
|
|
JS_CFUNC_DEF("includes", 1, js_array_includes ),
|
|
JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
|
|
JS_CFUNC_DEF("toString", 0, js_array_toString ),
|
|
JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
|
|
JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
|
|
JS_CFUNC_DEF("sort", 1, js_array_sort ),
|
|
JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
|
|
JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
|
|
JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
|
|
JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
|
|
JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
|
|
JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
|
|
JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
|
|
JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
|
|
};
|
|
|
|
void JS_AddIntrinsicArray(JSContext *ctx) {
|
|
JSValueConst obj;
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],
|
|
js_array_proto_funcs,
|
|
countof(js_array_proto_funcs));
|
|
obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
|
|
ctx->class_proto[JS_CLASS_ARRAY]);
|
|
ctx->array_ctor = JS_DupValue(ctx, obj);
|
|
JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
|
|
countof(js_array_funcs));
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
|
|
JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
|
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
|
|
};
|
|
|
|
void JS_AddArrayIteratorProto(JSContext *ctx) {
|
|
/* needed to initialize arguments[Symbol.iterator] */
|
|
ctx->array_proto_values =
|
|
JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
|
|
ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
|
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],
|
|
js_array_iterator_proto_funcs,
|
|
countof(js_array_iterator_proto_funcs));
|
|
}
|
|
|
|
int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, JSValue val, BOOL is_array_ctor)
|
|
{
|
|
uint32_t tag, len;
|
|
tag = JS_VALUE_GET_TAG(val);
|
|
switch(tag) {
|
|
case JS_TAG_INT:
|
|
case JS_TAG_BOOL:
|
|
case JS_TAG_NULL:
|
|
{
|
|
int v;
|
|
v = JS_VALUE_GET_INT(val);
|
|
if (v < 0)
|
|
goto fail;
|
|
len = v;
|
|
}
|
|
break;
|
|
#ifdef CONFIG_BIGNUM
|
|
case JS_TAG_BIG_INT:
|
|
case JS_TAG_BIG_FLOAT:
|
|
{
|
|
JSBigFloat *p = JS_VALUE_GET_PTR(val);
|
|
bf_t a;
|
|
BOOL res;
|
|
bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
|
|
bf_init(ctx->bf_ctx, &a);
|
|
bf_set_ui(&a, len);
|
|
res = bf_cmp_eq(&a, &p->num);
|
|
bf_delete(&a);
|
|
JS_FreeValue(ctx, val);
|
|
if (!res)
|
|
goto fail;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
if (JS_TAG_IS_FLOAT64(tag)) {
|
|
double d;
|
|
d = JS_VALUE_GET_FLOAT64(val);
|
|
len = (uint32_t)d;
|
|
if (len != d)
|
|
goto fail;
|
|
} else {
|
|
uint32_t len1;
|
|
if (is_array_ctor) {
|
|
val = JS_ToNumberFree(ctx, val);
|
|
if (JS_IsException(val))
|
|
return -1;
|
|
/* cannot recurse because val is a number */
|
|
if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
|
|
return -1;
|
|
} else {
|
|
/* legacy behavior: must do the conversion twice and compare */
|
|
if (JS_ToUint32(ctx, &len, val)) {
|
|
JS_FreeValue(ctx, val);
|
|
return -1;
|
|
}
|
|
val = JS_ToNumberFree(ctx, val);
|
|
if (JS_IsException(val))
|
|
return -1;
|
|
/* cannot recurse because val is a number */
|
|
if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
|
|
return -1;
|
|
if (len1 != len) {
|
|
fail:
|
|
JS_ThrowRangeError(ctx, "invalid array length");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
*plen = len;
|
|
return 0;
|
|
}
|
|
|
|
/* set the array length and remove the array elements if necessary. */
|
|
int set_array_length(JSContext *ctx, JSObject *p, JSValue val, int flags)
|
|
{
|
|
uint32_t len, idx, cur_len;
|
|
int i, ret;
|
|
/* Note: this call can reallocate the properties of 'p' */
|
|
ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
|
|
if (ret)
|
|
return -1;
|
|
/* JS_ToArrayLengthFree() must be done before the read-only test */
|
|
if (UNLIKELY(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))
|
|
return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
|
|
if (LIKELY(p->fast_array)) {
|
|
uint32_t old_len = p->u.array.count;
|
|
if (len < old_len) {
|
|
for(i = len; i < old_len; i++) {
|
|
JS_FreeValue(ctx, p->u.array.u.values[i]);
|
|
}
|
|
p->u.array.count = len;
|
|
}
|
|
p->prop[0].u.value = JS_NewUint32(ctx, len);
|
|
} else {
|
|
/* Note: length is always a uint32 because the object is an
|
|
array */
|
|
JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
|
|
if (len < cur_len) {
|
|
uint32_t d;
|
|
JSShape *sh;
|
|
JSShapeProperty *pr;
|
|
d = cur_len - len;
|
|
sh = p->shape;
|
|
if (d <= sh->prop_count) {
|
|
JSAtom atom;
|
|
/* faster to iterate */
|
|
while (cur_len > len) {
|
|
atom = JS_NewAtomUInt32(ctx, cur_len - 1);
|
|
ret = delete_property(ctx, p, atom);
|
|
JS_FreeAtom(ctx, atom);
|
|
if (UNLIKELY(!ret)) {
|
|
/* UNLIKELY case: property is not
|
|
configurable */
|
|
break;
|
|
}
|
|
cur_len--;
|
|
}
|
|
} else {
|
|
/* faster to iterate thru all the properties. Need two
|
|
passes in case one of the property is not
|
|
configurable */
|
|
cur_len = len;
|
|
for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
|
|
i++, pr++) {
|
|
if (pr->atom != JS_ATOM_NULL &&
|
|
JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
|
|
if (idx >= cur_len &&
|
|
!(pr->flags & JS_PROP_CONFIGURABLE)) {
|
|
cur_len = idx + 1;
|
|
}
|
|
}
|
|
}
|
|
for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
|
|
i++, pr++) {
|
|
if (pr->atom != JS_ATOM_NULL &&
|
|
JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
|
|
if (idx >= cur_len) {
|
|
/* remove the property */
|
|
delete_property(ctx, p, pr->atom);
|
|
/* WARNING: the shape may have been modified */
|
|
sh = p->shape;
|
|
pr = get_shape_prop(sh) + i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
cur_len = len;
|
|
}
|
|
set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
|
|
if (UNLIKELY(cur_len > len)) {
|
|
return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void js_array_finalizer(JSRuntime *rt, JSValue val)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
|
int i;
|
|
for(i = 0; i < p->u.array.count; i++) {
|
|
JS_FreeValueRT(rt, p->u.array.u.values[i]);
|
|
}
|
|
js_free_rt(rt, p->u.array.u.values);
|
|
}
|
|
|
|
void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
|
{
|
|
JSObject *p = JS_VALUE_GET_OBJ(val);
|
|
int i;
|
|
for(i = 0; i < p->u.array.count; i++) {
|
|
JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
|
|
}
|
|
}
|