cosmopolitan/third_party/quickjs/object.c
2021-08-05 14:43:53 -07:00

1222 lines
40 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 */
void js_object_data_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JS_FreeValueRT(rt, p->u.object_data);
p->u.object_data = JS_UNDEFINED;
}
void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JS_MarkValue(rt, p->u.object_data, mark_func);
}
static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSValue ret;
if (!JS_IsUndefined(new_target) &&
JS_VALUE_GET_OBJ(new_target) !=
JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
} else {
int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
switch(tag) {
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
ret = JS_NewObject(ctx);
break;
default:
ret = JS_ToObject(ctx, argv[0]);
break;
}
}
return ret;
}
static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
JSAtom prop, JSValueConst desc,
int flags)
{
JSPropertyDescriptor d;
int ret;
if (js_obj_to_desc(ctx, &d, desc) < 0)
return -1;
ret = JS_DefineProperty(ctx, obj, prop,
d.value, d.getter, d.setter, d.flags | flags);
js_free_desc(ctx, &d);
return ret;
}
static __exception int JS_ObjectDefineProperties(JSContext *ctx,
JSValueConst obj,
JSValueConst properties)
{
JSValue props, desc;
JSObject *p;
JSPropertyEnum *atoms;
uint32_t len, i;
int ret = -1;
if (!JS_IsObject(obj)) {
JS_ThrowTypeErrorNotAnObject(ctx);
return -1;
}
desc = JS_UNDEFINED;
props = JS_ToObject(ctx, properties);
if (JS_IsException(props))
return -1;
p = JS_VALUE_GET_OBJ(props);
if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
goto exception;
for(i = 0; i < len; i++) {
JS_FreeValue(ctx, desc);
desc = JS_GetProperty(ctx, props, atoms[i].atom);
if (JS_IsException(desc))
goto exception;
if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
goto exception;
}
ret = 0;
exception:
js_free_prop_enum(ctx, atoms, len);
JS_FreeValue(ctx, props);
JS_FreeValue(ctx, desc);
return ret;
}
static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst proto, props;
JSValue obj;
proto = argv[0];
if (!JS_IsObject(proto) && !JS_IsNull(proto))
return JS_ThrowTypeError(ctx, "not a prototype");
obj = JS_NewObjectProto(ctx, proto);
if (JS_IsException(obj))
return JS_EXCEPTION;
props = argv[1];
if (!JS_IsUndefined(props)) {
if (JS_ObjectDefineProperties(ctx, obj, props)) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
}
return obj;
}
JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSValueConst val;
val = argv[0];
if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
/* ES6 feature non compatible with ES5.1: primitive types are
accepted */
if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
return JS_ThrowTypeErrorNotAnObject(ctx);
}
return JS_GetPrototype(ctx, val);
}
static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst obj;
obj = argv[0];
if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
return JS_EXCEPTION;
return JS_DupValue(ctx, obj);
}
/* magic = 1 if called as Reflect.defineProperty */
JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSValueConst obj, prop, desc;
int ret, flags;
JSAtom atom;
obj = argv[0];
prop = argv[1];
desc = argv[2];
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
return JS_ThrowTypeErrorNotAnObject(ctx);
atom = JS_ValueToAtom(ctx, prop);
if (UNLIKELY(atom == JS_ATOM_NULL))
return JS_EXCEPTION;
flags = 0;
if (!magic)
flags |= JS_PROP_THROW;
ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
JS_FreeAtom(ctx, atom);
if (ret < 0) {
return JS_EXCEPTION;
} else if (magic) {
return JS_NewBool(ctx, ret);
} else {
return JS_DupValue(ctx, obj);
}
}
static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
// defineProperties(obj, properties)
JSValueConst obj = argv[0];
if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
return JS_EXCEPTION;
else
return JS_DupValue(ctx, obj);
}
/* magic = 1 if called as __defineSetter__ */
static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSValue obj;
JSValueConst prop, value, get, set;
int ret, flags;
JSAtom atom;
prop = argv[0];
value = argv[1];
obj = JS_ToObject(ctx, this_val);
if (JS_IsException(obj))
return JS_EXCEPTION;
if (check_function(ctx, value)) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
atom = JS_ValueToAtom(ctx, prop);
if (UNLIKELY(atom == JS_ATOM_NULL)) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
flags = JS_PROP_THROW |
JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
if (magic) {
get = JS_UNDEFINED;
set = value;
flags |= JS_PROP_HAS_SET;
} else {
get = value;
set = JS_UNDEFINED;
flags |= JS_PROP_HAS_GET;
}
ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
JS_FreeValue(ctx, obj);
JS_FreeAtom(ctx, atom);
if (ret < 0) {
return JS_EXCEPTION;
} else {
return JS_UNDEFINED;
}
}
JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic)
{
JSValueConst prop;
JSAtom atom;
JSValue ret, obj;
JSPropertyDescriptor desc;
int res, flags;
if (magic) {
/* Reflect.getOwnPropertyDescriptor case */
if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
return JS_ThrowTypeErrorNotAnObject(ctx);
obj = JS_DupValue(ctx, argv[0]);
} else {
obj = JS_ToObject(ctx, argv[0]);
if (JS_IsException(obj))
return obj;
}
prop = argv[1];
atom = JS_ValueToAtom(ctx, prop);
if (UNLIKELY(atom == JS_ATOM_NULL))
goto exception;
ret = JS_UNDEFINED;
if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
if (res < 0)
goto exception;
if (res) {
ret = JS_NewObject(ctx);
if (JS_IsException(ret))
goto exception1;
flags = JS_PROP_C_W_E | JS_PROP_THROW;
if (desc.flags & JS_PROP_GETSET) {
if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
|| JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
goto exception1;
} else {
if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
|| JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)
goto exception1;
}
if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0
|| JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)
goto exception1;
js_free_desc(ctx, &desc);
}
}
JS_FreeAtom(ctx, atom);
JS_FreeValue(ctx, obj);
return ret;
exception1:
js_free_desc(ctx, &desc);
JS_FreeValue(ctx, ret);
exception:
JS_FreeAtom(ctx, atom);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
//getOwnPropertyDescriptors(obj)
JSValue obj, r;
JSObject *p;
JSPropertyEnum *props;
uint32_t len, i;
r = JS_UNDEFINED;
obj = JS_ToObject(ctx, argv[0]);
if (JS_IsException(obj))
return JS_EXCEPTION;
p = JS_VALUE_GET_OBJ(obj);
if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
goto exception;
r = JS_NewObject(ctx);
if (JS_IsException(r))
goto exception;
for(i = 0; i < len; i++) {
JSValue atomValue, desc;
JSValueConst args[2];
atomValue = JS_AtomToValue(ctx, props[i].atom);
if (JS_IsException(atomValue))
goto exception;
args[0] = obj;
args[1] = atomValue;
desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
JS_FreeValue(ctx, atomValue);
if (JS_IsException(desc))
goto exception;
if (!JS_IsUndefined(desc)) {
if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
JS_PROP_C_W_E | JS_PROP_THROW) < 0)
goto exception;
}
}
js_free_prop_enum(ctx, props, len);
JS_FreeValue(ctx, obj);
return r;
exception:
js_free_prop_enum(ctx, props, len);
JS_FreeValue(ctx, obj);
JS_FreeValue(ctx, r);
return JS_EXCEPTION;
}
JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
{
int tag = JS_VALUE_GET_NORM_TAG(val);
JSValue obj;
switch(tag) {
default:
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
return JS_ThrowTypeError(ctx, "cannot convert to object");
case JS_TAG_OBJECT:
case JS_TAG_EXCEPTION:
return JS_DupValue(ctx, val);
#ifdef CONFIG_BIGNUM
case JS_TAG_BIG_INT:
obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
goto set_value;
case JS_TAG_BIG_FLOAT:
obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
goto set_value;
case JS_TAG_BIG_DECIMAL:
obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
goto set_value;
#endif
case JS_TAG_INT:
case JS_TAG_FLOAT64:
obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
goto set_value;
case JS_TAG_STRING:
/* XXX: should call the string constructor */
{
JSString *p1 = JS_VALUE_GET_STRING(val);
obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
}
goto set_value;
case JS_TAG_BOOL:
obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
goto set_value;
case JS_TAG_SYMBOL:
obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
set_value:
if (!JS_IsException(obj))
JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
return obj;
}
}
JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
{
JSValue obj = JS_ToObject(ctx, val);
JS_FreeValue(ctx, val);
return obj;
}
int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, JSValueConst desc)
{
JSValue val, getter, setter;
int flags;
if (!JS_IsObject(desc)) {
JS_ThrowTypeErrorNotAnObject(ctx);
return -1;
}
flags = 0;
val = JS_UNDEFINED;
getter = JS_UNDEFINED;
setter = JS_UNDEFINED;
if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
if (JS_IsException(prop))
goto fail;
flags |= JS_PROP_HAS_CONFIGURABLE;
if (JS_ToBoolFree(ctx, prop))
flags |= JS_PROP_CONFIGURABLE;
}
if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
if (JS_IsException(prop))
goto fail;
flags |= JS_PROP_HAS_WRITABLE;
if (JS_ToBoolFree(ctx, prop))
flags |= JS_PROP_WRITABLE;
}
if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
if (JS_IsException(prop))
goto fail;
flags |= JS_PROP_HAS_ENUMERABLE;
if (JS_ToBoolFree(ctx, prop))
flags |= JS_PROP_ENUMERABLE;
}
if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
flags |= JS_PROP_HAS_VALUE;
val = JS_GetProperty(ctx, desc, JS_ATOM_value);
if (JS_IsException(val))
goto fail;
}
if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
flags |= JS_PROP_HAS_GET;
getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
if (JS_IsException(getter) ||
!(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
JS_ThrowTypeError(ctx, "invalid getter");
goto fail;
}
}
if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
flags |= JS_PROP_HAS_SET;
setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
if (JS_IsException(setter) ||
!(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
JS_ThrowTypeError(ctx, "invalid setter");
goto fail;
}
}
if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
(flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
goto fail;
}
d->flags = flags;
d->value = val;
d->getter = getter;
d->setter = setter;
return 0;
fail:
JS_FreeValue(ctx, val);
JS_FreeValue(ctx, getter);
JS_FreeValue(ctx, setter);
return -1;
}
JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, int flags, int kind)
{
JSValue obj, r, val, key, value;
JSObject *p;
JSPropertyEnum *atoms;
uint32_t len, i, j;
r = JS_UNDEFINED;
val = JS_UNDEFINED;
obj = JS_ToObject(ctx, obj1);
if (JS_IsException(obj))
return JS_EXCEPTION;
p = JS_VALUE_GET_OBJ(obj);
if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
goto exception;
r = JS_NewArray(ctx);
if (JS_IsException(r))
goto exception;
for(j = i = 0; i < len; i++) {
JSAtom atom = atoms[i].atom;
if (flags & JS_GPN_ENUM_ONLY) {
JSPropertyDescriptor desc;
int res;
/* Check if property is still enumerable */
res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
if (res < 0)
goto exception;
if (!res)
continue;
js_free_desc(ctx, &desc);
if (!(desc.flags & JS_PROP_ENUMERABLE))
continue;
}
switch(kind) {
default:
case JS_ITERATOR_KIND_KEY:
val = JS_AtomToValue(ctx, atom);
if (JS_IsException(val))
goto exception;
break;
case JS_ITERATOR_KIND_VALUE:
val = JS_GetProperty(ctx, obj, atom);
if (JS_IsException(val))
goto exception;
break;
case JS_ITERATOR_KIND_KEY_AND_VALUE:
val = JS_NewArray(ctx);
if (JS_IsException(val))
goto exception;
key = JS_AtomToValue(ctx, atom);
if (JS_IsException(key))
goto exception1;
if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
goto exception1;
value = JS_GetProperty(ctx, obj, atom);
if (JS_IsException(value))
goto exception1;
if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
goto exception1;
break;
}
if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
goto exception;
}
goto done;
exception1:
JS_FreeValue(ctx, val);
exception:
JS_FreeValue(ctx, r);
r = JS_EXCEPTION;
done:
js_free_prop_enum(ctx, atoms, len);
JS_FreeValue(ctx, obj);
return r;
}
static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_GetOwnPropertyNames2(ctx, argv[0],
JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
}
static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_GetOwnPropertyNames2(ctx, argv[0],
JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
}
JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int kind)
{
return JS_GetOwnPropertyNames2(ctx, argv[0],
JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
}
JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int reflect)
{
JSValueConst obj;
int ret;
obj = argv[0];
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
if (reflect)
return JS_ThrowTypeErrorNotAnObject(ctx);
else
return JS_FALSE;
}
ret = JS_IsExtensible(ctx, obj);
if (ret < 0)
return JS_EXCEPTION;
else
return JS_NewBool(ctx, ret);
}
JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int reflect)
{
JSValueConst obj;
int ret;
obj = argv[0];
if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
if (reflect)
return JS_ThrowTypeErrorNotAnObject(ctx);
else
return JS_DupValue(ctx, obj);
}
ret = JS_PreventExtensions(ctx, obj);
if (ret < 0)
return JS_EXCEPTION;
if (reflect) {
return JS_NewBool(ctx, ret);
} else {
if (!ret)
return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
return JS_DupValue(ctx, obj);
}
}
static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue obj;
JSAtom atom;
JSObject *p;
BOOL ret;
atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
if (UNLIKELY(atom == JS_ATOM_NULL))
return JS_EXCEPTION;
obj = JS_ToObject(ctx, this_val);
if (JS_IsException(obj)) {
JS_FreeAtom(ctx, atom);
return obj;
}
p = JS_VALUE_GET_OBJ(obj);
ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
JS_FreeAtom(ctx, atom);
JS_FreeValue(ctx, obj);
if (ret < 0)
return JS_EXCEPTION;
else
return JS_NewBool(ctx, ret);
}
static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_ToObject(ctx, this_val);
}
JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue obj, tag;
int is_array;
JSAtom atom;
JSObject *p;
if (JS_IsNull(this_val)) {
tag = JS_NewString(ctx, "Null");
} else if (JS_IsUndefined(this_val)) {
tag = JS_NewString(ctx, "Undefined");
} else {
obj = JS_ToObject(ctx, this_val);
if (JS_IsException(obj))
return obj;
is_array = JS_IsArray(ctx, obj);
if (is_array < 0) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
if (is_array) {
atom = JS_ATOM_Array;
} else if (JS_IsFunction(ctx, obj)) {
atom = JS_ATOM_Function;
} else {
p = JS_VALUE_GET_OBJ(obj);
switch(p->class_id) {
case JS_CLASS_STRING:
case JS_CLASS_ARGUMENTS:
case JS_CLASS_MAPPED_ARGUMENTS:
case JS_CLASS_ERROR:
case JS_CLASS_BOOLEAN:
case JS_CLASS_NUMBER:
case JS_CLASS_DATE:
case JS_CLASS_REGEXP:
atom = ctx->rt->class_array[p->class_id].class_name;
break;
default:
atom = JS_ATOM_Object;
break;
}
}
tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
JS_FreeValue(ctx, obj);
if (JS_IsException(tag))
return JS_EXCEPTION;
if (!JS_IsString(tag)) {
JS_FreeValue(ctx, tag);
tag = JS_AtomToString(ctx, atom);
}
}
return JS_ConcatString3(ctx, "[object ", tag, "]");
}
static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
}
static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
// Object.assign(obj, source1)
JSValue obj, s;
int i;
s = JS_UNDEFINED;
obj = JS_ToObject(ctx, argv[0]);
if (JS_IsException(obj))
goto exception;
for (i = 1; i < argc; i++) {
if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
s = JS_ToObject(ctx, argv[i]);
if (JS_IsException(s))
goto exception;
if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
goto exception;
JS_FreeValue(ctx, s);
}
}
return obj;
exception:
JS_FreeValue(ctx, obj);
JS_FreeValue(ctx, s);
return JS_EXCEPTION;
}
JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int freeze_flag)
{
JSValueConst obj = argv[0];
JSObject *p;
JSPropertyEnum *props;
uint32_t len, i;
int flags, desc_flags, res;
if (!JS_IsObject(obj))
return JS_DupValue(ctx, obj);
res = JS_PreventExtensions(ctx, obj);
if (res < 0)
return JS_EXCEPTION;
if (!res) {
return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
}
p = JS_VALUE_GET_OBJ(obj);
flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
return JS_EXCEPTION;
for(i = 0; i < len; i++) {
JSPropertyDescriptor desc;
JSAtom prop = props[i].atom;
desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
if (freeze_flag) {
res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
if (res < 0)
goto exception;
if (res) {
if (desc.flags & JS_PROP_WRITABLE)
desc_flags |= JS_PROP_HAS_WRITABLE;
js_free_desc(ctx, &desc);
}
}
if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
goto exception;
}
js_free_prop_enum(ctx, props, len);
return JS_DupValue(ctx, obj);
exception:
js_free_prop_enum(ctx, props, len);
return JS_EXCEPTION;
}
static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int is_frozen)
{
JSValueConst obj = argv[0];
JSObject *p;
JSPropertyEnum *props;
uint32_t len, i;
int flags, res;
if (!JS_IsObject(obj))
return JS_TRUE;
p = JS_VALUE_GET_OBJ(obj);
flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
return JS_EXCEPTION;
for(i = 0; i < len; i++) {
JSPropertyDescriptor desc;
JSAtom prop = props[i].atom;
res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
if (res < 0)
goto exception;
if (res) {
js_free_desc(ctx, &desc);
if ((desc.flags & JS_PROP_CONFIGURABLE)
|| (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
res = FALSE;
goto done;
}
}
}
res = JS_IsExtensible(ctx, obj);
if (res < 0)
return JS_EXCEPTION;
res ^= 1;
done:
js_free_prop_enum(ctx, props, len);
return JS_NewBool(ctx, res);
exception:
js_free_prop_enum(ctx, props, len);
return JS_EXCEPTION;
}
static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue obj, iter, next_method = JS_UNDEFINED;
JSValueConst iterable;
BOOL done;
/* RequireObjectCoercible() not necessary because it is tested in
JS_GetIterator() by JS_GetProperty() */
iterable = argv[0];
obj = JS_NewObject(ctx);
if (JS_IsException(obj))
return obj;
iter = JS_GetIterator(ctx, iterable, FALSE);
if (JS_IsException(iter))
goto fail;
next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
if (JS_IsException(next_method))
goto fail;
for(;;) {
JSValue key, value, item;
item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
if (JS_IsException(item))
goto fail;
if (done) {
JS_FreeValue(ctx, item);
break;
}
key = JS_UNDEFINED;
value = JS_UNDEFINED;
if (!JS_IsObject(item)) {
JS_ThrowTypeErrorNotAnObject(ctx);
goto fail1;
}
key = JS_GetPropertyUint32(ctx, item, 0);
if (JS_IsException(key))
goto fail1;
value = JS_GetPropertyUint32(ctx, item, 1);
if (JS_IsException(value)) {
JS_FreeValue(ctx, key);
goto fail1;
}
if (JS_DefinePropertyValueValue(ctx, obj, key, value,
JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
fail1:
JS_FreeValue(ctx, item);
goto fail;
}
JS_FreeValue(ctx, item);
}
JS_FreeValue(ctx, next_method);
JS_FreeValue(ctx, iter);
return obj;
fail:
if (JS_IsObject(iter)) {
/* close the iterator object, preserving pending exception */
JS_IteratorClose(ctx, iter, TRUE);
}
JS_FreeValue(ctx, next_method);
JS_FreeValue(ctx, iter);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
#if 0
/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */
static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int ret;
ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),
JS_DupValue(ctx, argv[2]),
JS_PROP_C_W_E | JS_PROP_THROW);
if (ret < 0)
return JS_EXCEPTION;
else
return JS_NewBool(ctx, ret);
}
static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_ToObject(ctx, argv[0]);
}
static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int hint = HINT_NONE;
if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)
hint = JS_VALUE_GET_INT(argv[1]);
return JS_ToPrimitive(ctx, argv[0], hint);
}
#endif
/* return an empty string if not an object */
static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSAtom atom;
JSObject *p;
uint32_t tag;
int class_id;
tag = JS_VALUE_GET_NORM_TAG(argv[0]);
if (tag == JS_TAG_OBJECT) {
p = JS_VALUE_GET_OBJ(argv[0]);
class_id = p->class_id;
if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))
class_id = JS_CLASS_BYTECODE_FUNCTION;
atom = ctx->rt->class_array[class_id].class_name;
} else {
atom = JS_ATOM_empty_string;
}
return JS_AtomToString(ctx, atom);
}
static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
}
#if 0
static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_GetObjectData(ctx, argv[0]);
}
static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))
return JS_EXCEPTION;
return JS_DupValue(ctx, argv[1]);
}
static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_ToPropertyKey(ctx, argv[0]);
}
static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewBool(ctx, JS_IsObject(argv[0]));
}
static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));
}
static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));
}
#endif
JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj, JSValueConst defaultConstructor)
{
JSValue ctor, species;
if (!JS_IsObject(obj))
return JS_ThrowTypeErrorNotAnObject(ctx);
ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
if (JS_IsException(ctor))
return ctor;
if (JS_IsUndefined(ctor))
return JS_DupValue(ctx, defaultConstructor);
if (!JS_IsObject(ctor)) {
JS_FreeValue(ctx, ctor);
return JS_ThrowTypeErrorNotAnObject(ctx);
}
species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
JS_FreeValue(ctx, ctor);
if (JS_IsException(species))
return species;
if (JS_IsUndefined(species) || JS_IsNull(species))
return JS_DupValue(ctx, defaultConstructor);
if (!JS_IsConstructor(ctx, species)) {
JS_FreeValue(ctx, species);
return JS_ThrowTypeError(ctx, "not a constructor");
}
return species;
}
#if 0
static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_SpeciesConstructor(ctx, argv[0], argv[1]);
}
#endif
static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
{
JSValue val, ret;
val = JS_ToObject(ctx, this_val);
if (JS_IsException(val))
return val;
ret = JS_GetPrototype(ctx, val);
JS_FreeValue(ctx, val);
return ret;
}
static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
JSValueConst proto)
{
if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
return JS_ThrowTypeErrorNotAnObject(ctx);
if (!JS_IsObject(proto) && !JS_IsNull(proto))
return JS_UNDEFINED;
if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
return JS_EXCEPTION;
else
return JS_UNDEFINED;
}
static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue obj, v1;
JSValueConst v;
int res;
v = argv[0];
if (!JS_IsObject(v))
return JS_FALSE;
obj = JS_ToObject(ctx, this_val);
if (JS_IsException(obj))
return JS_EXCEPTION;
v1 = JS_DupValue(ctx, v);
for(;;) {
v1 = JS_GetPrototypeFree(ctx, v1);
if (JS_IsException(v1))
goto exception;
if (JS_IsNull(v1)) {
res = FALSE;
break;
}
if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
res = TRUE;
break;
}
/* avoid infinite loop (possible with proxies) */
if (js_poll_interrupts(ctx))
goto exception;
}
JS_FreeValue(ctx, v1);
JS_FreeValue(ctx, obj);
return JS_NewBool(ctx, res);
exception:
JS_FreeValue(ctx, v1);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValue obj, res = JS_EXCEPTION;
JSAtom prop = JS_ATOM_NULL;
JSPropertyDescriptor desc;
int has_prop;
obj = JS_ToObject(ctx, this_val);
if (JS_IsException(obj))
goto exception;
prop = JS_ValueToAtom(ctx, argv[0]);
if (UNLIKELY(prop == JS_ATOM_NULL))
goto exception;
has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
if (has_prop < 0)
goto exception;
if (has_prop) {
res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);
js_free_desc(ctx, &desc);
} else {
res = JS_FALSE;
}
exception:
JS_FreeAtom(ctx, prop);
JS_FreeValue(ctx, obj);
return res;
}
static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int setter)
{
JSValue obj, res = JS_EXCEPTION;
JSAtom prop = JS_ATOM_NULL;
JSPropertyDescriptor desc;
int has_prop;
obj = JS_ToObject(ctx, this_val);
if (JS_IsException(obj))
goto exception;
prop = JS_ValueToAtom(ctx, argv[0]);
if (UNLIKELY(prop == JS_ATOM_NULL))
goto exception;
for (;;) {
has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
if (has_prop < 0)
goto exception;
if (has_prop) {
if (desc.flags & JS_PROP_GETSET)
res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
else
res = JS_UNDEFINED;
js_free_desc(ctx, &desc);
break;
}
obj = JS_GetPrototypeFree(ctx, obj);
if (JS_IsException(obj))
goto exception;
if (JS_IsNull(obj)) {
res = JS_UNDEFINED;
break;
}
/* avoid infinite loop (possible with proxies) */
if (js_poll_interrupts(ctx))
goto exception;
}
exception:
JS_FreeAtom(ctx, prop);
JS_FreeValue(ctx, obj);
return res;
}
static const JSCFunctionListEntry js_object_funcs[] = {
JS_CFUNC_DEF("create", 2, js_object_create ),
JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
JS_CFUNC_DEF("is", 2, js_object_is ),
JS_CFUNC_DEF("assign", 2, js_object_assign ),
JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),
//JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),
//JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),
//JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),
//JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),
//JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),
//JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),
//JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),
//JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),
//JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),
//JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),
JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
};
static const JSCFunctionListEntry js_object_proto_funcs[] = {
JS_CFUNC_DEF("toString", 0, js_object_toString ),
JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
};
void JS_AddIntrinsicObject(JSContext *ctx) {
JSValueConst obj;
obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,
ctx->class_proto[JS_CLASS_OBJECT]);
JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
js_object_proto_funcs, countof(js_object_proto_funcs));
}