/* * 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)); }