| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 Crimson AS <info@crimson.no> |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtQml module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| |
| #include "qv4objectproto_p.h" |
| #include "qv4argumentsobject_p.h" |
| #include <private/qv4mm_p.h> |
| #include "qv4scopedvalue_p.h" |
| #include "qv4runtime_p.h" |
| #include "qv4objectiterator_p.h" |
| #include "qv4string_p.h" |
| #include "qv4jscall_p.h" |
| #include "qv4symbol_p.h" |
| #include "qv4propertykey_p.h" |
| |
| #include <QtCore/QDateTime> |
| #include <QtCore/QStringList> |
| |
| using namespace QV4; |
| |
| |
| DEFINE_OBJECT_VTABLE(ObjectCtor); |
| |
| void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) |
| { |
| Heap::FunctionObject::init(scope, QStringLiteral("Object")); |
| } |
| |
| ReturnedValue ObjectCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
| { |
| ExecutionEngine *v4 = f->engine(); |
| const ObjectCtor *nt = static_cast<const ObjectCtor *>(newTarget); |
| if (!argc || argv[0].isUndefined() || argv[0].isNull()) { |
| Scope scope(v4); |
| ScopedObject obj(scope, scope.engine->newObject()); |
| ScopedObject proto(scope, nt->get(scope.engine->id_prototype())); |
| if (!!proto) |
| obj->setPrototypeOf(proto); |
| return obj.asReturnedValue(); |
| } else { |
| return argv[0].toObject(v4)->asReturnedValue(); |
| } |
| } |
| |
| ReturnedValue ObjectCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc) |
| { |
| ExecutionEngine *v4 = m->engine(); |
| if (!argc || argv[0].isUndefined() || argv[0].isNull()) { |
| return v4->newObject()->asReturnedValue(); |
| } else { |
| return argv[0].toObject(v4)->asReturnedValue(); |
| } |
| } |
| |
| void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) |
| { |
| Scope scope(v4); |
| ScopedObject o(scope, this); |
| |
| ctor->defineReadonlyProperty(v4->id_prototype(), o); |
| ctor->defineReadonlyConfigurableProperty(v4->id_length(), Value::fromInt32(1)); |
| ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); |
| ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); |
| ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptors"), method_getOwnPropertyDescriptors, 1); |
| ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); |
| ctor->defineDefaultProperty(QStringLiteral("getOwnPropertySymbols"), method_getOwnPropertySymbols, 1); |
| ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2); |
| ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2); |
| ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3); |
| ctor->defineDefaultProperty(QStringLiteral("defineProperties"), method_defineProperties, 2); |
| ctor->defineDefaultProperty(QStringLiteral("entries"), method_entries, 1); |
| ctor->defineDefaultProperty(QStringLiteral("seal"), method_seal, 1); |
| ctor->defineDefaultProperty(QStringLiteral("freeze"), method_freeze, 1); |
| ctor->defineDefaultProperty(QStringLiteral("preventExtensions"), method_preventExtensions, 1); |
| ctor->defineDefaultProperty(QStringLiteral("is"), method_is, 2); |
| ctor->defineDefaultProperty(QStringLiteral("isSealed"), method_isSealed, 1); |
| ctor->defineDefaultProperty(QStringLiteral("isFrozen"), method_isFrozen, 1); |
| ctor->defineDefaultProperty(QStringLiteral("isExtensible"), method_isExtensible, 1); |
| ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1); |
| ctor->defineDefaultProperty(QStringLiteral("setPrototypeOf"), method_setPrototypeOf, 2); |
| ctor->defineDefaultProperty(QStringLiteral("values"), method_values, 1); |
| |
| defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); |
| defineDefaultProperty(v4->id_toString(), method_toString, 0); |
| defineDefaultProperty(v4->id_toLocaleString(), method_toLocaleString, 0); |
| defineDefaultProperty(v4->id_valueOf(), method_valueOf, 0); |
| defineDefaultProperty(QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); |
| defineDefaultProperty(QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); |
| defineDefaultProperty(QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); |
| defineDefaultProperty(QStringLiteral("__defineGetter__"), method_defineGetter, 2); |
| defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2); |
| |
| defineAccessorProperty(v4->id___proto__(), method_get_proto, method_set_proto); |
| } |
| |
| ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (argc < 1) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject o(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedObject p(scope, o->getPrototypeOf()); |
| return (!!p ? p->asReturnedValue() : Encode::null()); |
| } |
| |
| ReturnedValue ObjectPrototype::method_is(const FunctionObject *, const Value *, const Value *argv, int argc) |
| { |
| if (!argc) |
| return Encode(true); |
| if (argc == 1) |
| return Encode((argv[0].isUndefined() ? true : false)); |
| return Encode(argv[0].sameValue(argv[1])); |
| } |
| |
| ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (argc < 1) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject O(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| if (ArgumentsObject::isNonStrictArgumentsObject(O)) |
| static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); |
| |
| ScopedValue v(scope, argc > 1 ? argv[1] : Value::undefinedValue()); |
| ScopedPropertyKey name(scope, v->toPropertyKey(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedProperty desc(scope); |
| PropertyAttributes attrs = O->getOwnProperty(name, desc); |
| return fromPropertyDescriptor(scope.engine, desc, attrs); |
| } |
| |
| ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptors(const FunctionObject *f, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(f); |
| if (!argc) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject o(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return Encode::undefined(); |
| |
| ScopedObject descriptors(scope, scope.engine->newObject()); |
| |
| ObjectIterator it(scope, o, ObjectIterator::WithSymbols); |
| ScopedProperty pd(scope); |
| PropertyAttributes attrs; |
| ScopedPropertyKey key(scope); |
| ScopedObject entry(scope); |
| while (1) { |
| key = it.next(pd, &attrs); |
| if (!key->isValid()) |
| break; |
| entry = fromPropertyDescriptor(scope.engine, pd, attrs); |
| descriptors->put(key, entry); |
| } |
| |
| return descriptors.asReturnedValue(); |
| |
| } |
| |
| ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (argc < 1) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject O(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| return Encode(getOwnPropertyNames(scope.engine, argv[0])); |
| } |
| |
| ReturnedValue ObjectPrototype::method_getOwnPropertySymbols(const FunctionObject *f, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(f); |
| if (!argc) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject O(scope, argv[0].toObject(scope.engine)); |
| if (!O) |
| return Encode::undefined(); |
| |
| ScopedArrayObject array(scope, scope.engine->newArrayObject()); |
| if (O) { |
| ObjectIterator it(scope, O, ObjectIterator::WithSymbols); |
| ScopedValue name(scope); |
| while (1) { |
| name = it.nextPropertyNameAsString(); |
| if (name->isNull()) |
| break; |
| if (!name->isSymbol()) |
| continue; |
| array->push_back(name); |
| } |
| } |
| return array->asReturnedValue(); |
| } |
| |
| // 19.1.2.1 |
| ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (argc < 1) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject to(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| if (argc == 1) |
| return to.asReturnedValue(); |
| |
| for (int i = 1, ei = argc; i < ei; ++i) { |
| if (argv[i].isUndefined() || argv[i].isNull()) |
| continue; |
| |
| ScopedObject from(scope, argv[i].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from)); |
| quint32 length = keys->getLength(); |
| |
| ScopedString nextKey(scope); |
| ScopedValue propValue(scope); |
| for (quint32 i = 0; i < length; ++i) { |
| nextKey = Value::fromReturnedValue(keys->get(i)).toString(scope.engine); |
| |
| ScopedProperty prop(scope); |
| PropertyAttributes attrs = from->getOwnProperty(nextKey->toPropertyKey(), prop); |
| |
| if (attrs == PropertyFlag::Attr_Invalid) |
| continue; |
| |
| if (!attrs.isEnumerable()) |
| continue; |
| |
| propValue = from->get(nextKey); |
| to->set(nextKey, propValue, Object::DoThrowOnRejection); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| } |
| } |
| |
| return to.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(builtin); |
| if (!argc || (!argv[0].isObject() && !argv[0].isNull())) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject O(scope, argv[0]); |
| |
| ScopedObject newObject(scope, scope.engine->newObject()); |
| newObject->setPrototypeOf(O); |
| |
| |
| if (argc > 1 && !argv[1].isUndefined()) { |
| Value *arguments = scope.alloc(argc); |
| arguments[0] = newObject; |
| memcpy(arguments + 1, argv + 1, (argc - 1)*sizeof(Value)); |
| return method_defineProperties(builtin, thisObject, arguments, argc); |
| } |
| |
| return newObject.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (!argc || !argv[0].isObject()) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject O(scope, argv[0]); |
| ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Value::undefinedValue()).toPropertyKey(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue()); |
| ScopedProperty pd(scope); |
| PropertyAttributes attrs; |
| toPropertyDescriptor(scope.engine, attributes, pd, &attrs); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| if (!O->defineOwnProperty(name, pd, attrs)) |
| THROW_TYPE_ERROR(); |
| |
| return O.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (argc < 2 || !argv[0].isObject()) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject O(scope, argv[0]); |
| |
| ScopedObject o(scope, argv[1].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedValue val(scope); |
| |
| ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
| ScopedProperty pd(scope); |
| ScopedProperty n(scope); |
| ScopedPropertyKey key(scope); |
| while (1) { |
| PropertyAttributes attrs; |
| key = it.next(pd, &attrs); |
| if (!key->isValid()) |
| break; |
| PropertyAttributes nattrs; |
| val = o->getValue(pd->value, attrs); |
| toPropertyDescriptor(scope.engine, val, n, &nattrs); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| bool ok = O->defineOwnProperty(key, n, nattrs); |
| if (!ok) |
| THROW_TYPE_ERROR(); |
| } |
| |
| return O.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_entries(const FunctionObject *f, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(f); |
| if (!argc) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject o(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return Encode::undefined(); |
| |
| ScopedArrayObject a(scope, scope.engine->newArrayObject()); |
| |
| ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
| ScopedString name(scope); |
| ScopedArrayObject entry(scope); |
| while (1) { |
| name = it.nextPropertyNameAsString(); |
| if (!name) |
| break; |
| entry = scope.engine->newArrayObject(); |
| entry->push_back(name); |
| a->push_back(entry); |
| } |
| |
| // now add values, do this after the loop above as reading out the values can have side effects |
| uint len = a->getLength(); |
| ScopedValue value(scope); |
| for (uint i = 0; i < len; ++i) { |
| entry = a->get(PropertyKey::fromArrayIndex(i)); |
| name = entry->get(PropertyKey::fromArrayIndex(0)); |
| value = o->get(name->toPropertyKey()); |
| if (scope.engine->hasException) |
| return Encode::undefined(); |
| entry->push_back(value); |
| } |
| |
| return a.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| const Value a = argc ? argv[0] : Value::undefinedValue(); |
| if (!a.isObject()) |
| // 19.1.2.17, 1 |
| return a.asReturnedValue(); |
| |
| Scope scope(b); |
| ScopedObject o(scope, a); |
| o->setInternalClass(o->internalClass()->canned()); |
| |
| if (o->arrayData()) { |
| ArrayData::ensureAttributes(o); |
| for (uint i = 0; i < o->d()->arrayData->values.alloc; ++i) { |
| if (!o->arrayData()->isEmpty(i)) |
| o->d()->arrayData->attrs[i].setConfigurable(false); |
| } |
| } |
| |
| return o.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| const Value a = argc ? argv[0] : Value::undefinedValue(); |
| if (!a.isObject()) |
| // 19.1.2.5, 1 |
| return a.asReturnedValue(); |
| |
| Scope scope(b); |
| ScopedObject o(scope, a); |
| |
| if (ArgumentsObject::isNonStrictArgumentsObject(o)) |
| static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); |
| |
| o->setInternalClass(o->internalClass()->cryopreserved()); |
| |
| if (o->arrayData()) { |
| ArrayData::ensureAttributes(o); |
| for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { |
| if (!o->arrayData()->isEmpty(i)) |
| o->arrayData()->attrs[i].setConfigurable(false); |
| if (o->arrayData()->attrs[i].isData()) |
| o->arrayData()->attrs[i].setWritable(false); |
| } |
| } |
| return o.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (!argc) |
| return Encode::undefined(); |
| |
| ScopedObject o(scope, argv[0]); |
| if (!o) |
| return argv[0].asReturnedValue(); |
| |
| o->preventExtensions(); |
| return o.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (!argc) |
| return Encode(true); |
| |
| ScopedObject o(scope, argv[0]); |
| if (!o) |
| return Encode(true); |
| |
| if (o->isExtensible()) |
| return Encode(false); |
| |
| if (o->internalClass() != o->internalClass()->canned()) |
| return Encode(false); |
| |
| if (!o->arrayData() || !o->arrayData()->length()) |
| return Encode(true); |
| |
| Q_ASSERT(o->arrayData() && o->arrayData()->length()); |
| if (!o->arrayData()->attrs) |
| return Encode(false); |
| |
| for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { |
| if (!o->arrayData()->isEmpty(i)) |
| if (o->arrayData()->attributes(i).isConfigurable()) |
| return Encode(false); |
| } |
| |
| return Encode(true); |
| } |
| |
| ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (!argc) |
| return Encode(true); |
| |
| ScopedObject o(scope, argv[0]); |
| if (!o) |
| return Encode(true); |
| |
| if (o->isExtensible()) |
| return Encode(false); |
| |
| if (!o->internalClass()->isImplicitlyFrozen()) |
| return Encode(false); |
| |
| if (!o->arrayData() || !o->arrayData()->length()) |
| return Encode(true); |
| |
| Q_ASSERT(o->arrayData() && o->arrayData()->length()); |
| if (!o->arrayData()->attrs) |
| return Encode(false); |
| |
| for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { |
| if (!o->arrayData()->isEmpty(i)) |
| if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) |
| return Encode(false); |
| } |
| |
| return Encode(true); |
| } |
| |
| ReturnedValue ObjectPrototype::method_isExtensible(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (!argc) |
| return Encode(false); |
| |
| ScopedObject o(scope, argv[0]); |
| if (!o) |
| return Encode(false); |
| |
| return Encode((bool)o->isExtensible()); |
| } |
| |
| ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (!argc) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject o(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedArrayObject a(scope, scope.engine->newArrayObject()); |
| |
| ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
| ScopedValue name(scope); |
| ScopedValue value(scope); |
| while (1) { |
| name = it.nextPropertyNameAsString(value); |
| if (name->isNull()) |
| break; |
| a->push_back(name); |
| } |
| |
| return a.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(f->engine()); |
| if (argc < 2 || argv[0].isNullOrUndefined() || !(argv[1].isObject() || argv[1].isNull())) |
| return scope.engine->throwTypeError(); |
| |
| if (!argv[0].isObject()) |
| return argv[0].asReturnedValue(); |
| |
| ScopedObject o(scope, argv[0]); |
| const Object *p = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1); |
| bool ok = o->setPrototypeOf(p); |
| if (!ok) |
| return scope.engine->throwTypeError(QStringLiteral("Could not change prototype.")); |
| return o->asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_values(const FunctionObject *f, const Value *, const Value *argv, int argc) |
| { |
| Scope scope(f); |
| if (!argc) |
| return scope.engine->throwTypeError(); |
| |
| ScopedObject o(scope, argv[0].toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedArrayObject a(scope, scope.engine->newArrayObject()); |
| |
| ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
| ScopedPropertyKey key(scope); |
| ScopedProperty pd(scope); |
| ScopedValue value(scope); |
| PropertyAttributes attrs; |
| while (1) { |
| key = it.next(pd, &attrs); |
| if (!key->isValid()) |
| break; |
| value = o->getValue(pd->value, attrs); |
| a->push_back(value); |
| } |
| |
| return a.asReturnedValue(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) |
| { |
| ExecutionEngine *v4 = b->engine(); |
| QString string; |
| if (thisObject->isUndefined()) { |
| string = QStringLiteral("[object Undefined]"); |
| } else if (thisObject->isNull()) { |
| string = QStringLiteral("[object Null]"); |
| } else { |
| const Object *o = thisObject->as<Object>(); |
| if (!o) { |
| // primitive, get the proper prototype |
| if (thisObject->isBoolean()) |
| o = v4->booleanPrototype(); |
| else if (thisObject->isNumber()) |
| o = v4->numberPrototype(); |
| else if (thisObject->isString()) |
| o = v4->stringPrototype(); |
| else if (thisObject->isSymbol()) |
| o = v4->symbolPrototype(); |
| Q_ASSERT(o); |
| } |
| QString name = o->className(); |
| Scope scope(v4); |
| ScopedString toStringTag(scope, o->get(v4->symbol_toStringTag())); |
| if (toStringTag) |
| name = toStringTag->toQString(); |
| string = QStringLiteral("[object %1]").arg(name); |
| } |
| return Encode(v4->newString(string)); |
| } |
| |
| ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| CHECK_STACK_LIMITS(scope.engine) |
| ScopedObject o(scope, thisObject->toObject(scope.engine)); |
| if (!o) |
| RETURN_UNDEFINED(); |
| |
| ScopedFunctionObject f(scope, o->get(scope.engine->id_toString())); |
| if (!f) |
| THROW_TYPE_ERROR(); |
| |
| return f->call(thisObject, argv, argc); |
| } |
| |
| ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) |
| { |
| return Encode(thisObject->toObject(b->engine())); |
| } |
| |
| ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| ScopedPropertyKey P(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| ScopedObject O(scope, thisObject->toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| bool r = O->getOwnProperty(P) != Attr_Invalid; |
| return Encode(r); |
| } |
| |
| ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (!argc || !argv[0].isObject()) |
| return Encode(false); |
| |
| ScopedObject V(scope, argv[0]); |
| ScopedObject O(scope, thisObject->toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| ScopedObject proto(scope, V->getPrototypeOf()); |
| while (proto) { |
| if (O->d() == proto->d()) |
| return Encode(true); |
| proto = proto->getPrototypeOf(); |
| } |
| return Encode(false); |
| } |
| |
| ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| ScopedPropertyKey p(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedObject o(scope, thisObject->toObject(scope.engine)); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| PropertyAttributes attrs = o->getOwnProperty(p); |
| return Encode(attrs.isEnumerable()); |
| } |
| |
| ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (argc < 2) |
| THROW_TYPE_ERROR(); |
| |
| ScopedFunctionObject f(scope, argv[1]); |
| if (!f) |
| THROW_TYPE_ERROR(); |
| |
| ScopedString prop(scope, argv[0], ScopedString::Convert); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedObject o(scope, thisObject); |
| if (!o) { |
| if (!thisObject->isUndefined()) |
| RETURN_UNDEFINED(); |
| o = scope.engine->globalObject; |
| } |
| |
| ScopedProperty pd(scope); |
| pd->value = f; |
| pd->set = Value::emptyValue(); |
| bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor); |
| if (!ok) |
| THROW_TYPE_ERROR(); |
| RETURN_UNDEFINED(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| if (argc < 2) |
| THROW_TYPE_ERROR(); |
| |
| ScopedFunctionObject f(scope, argv[1]); |
| if (!f) |
| THROW_TYPE_ERROR(); |
| |
| ScopedString prop(scope, argv[0], ScopedString::Convert); |
| if (scope.engine->hasException) |
| return QV4::Encode::undefined(); |
| |
| ScopedObject o(scope, thisObject); |
| if (!o) { |
| if (!thisObject->isUndefined()) |
| RETURN_UNDEFINED(); |
| o = scope.engine->globalObject; |
| } |
| |
| ScopedProperty pd(scope); |
| pd->value = Value::emptyValue(); |
| pd->set = f; |
| bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor); |
| if (!ok) |
| THROW_TYPE_ERROR(); |
| RETURN_UNDEFINED(); |
| } |
| |
| ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const Value *thisObject, const Value *, int) |
| { |
| Scope scope(b); |
| ScopedObject o(scope, thisObject->as<Object>()); |
| if (!o) |
| THROW_TYPE_ERROR(); |
| |
| return Encode(o->getPrototypeOf()); |
| } |
| |
| ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
| { |
| Scope scope(b); |
| ScopedObject o(scope, thisObject); |
| if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull())) |
| THROW_TYPE_ERROR(); |
| |
| const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv); |
| bool ok = o->setPrototypeOf(p); |
| if (!ok) |
| return scope.engine->throwTypeError(QStringLiteral("Could not change prototype.")); |
| return Encode::undefined(); |
| RETURN_UNDEFINED(); |
| } |
| |
| void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs) |
| { |
| Scope scope(engine); |
| ScopedObject o(scope, v); |
| if (!o) { |
| engine->throwTypeError(); |
| return; |
| } |
| |
| attrs->clear(); |
| desc->value = Value::emptyValue(); |
| desc->set = Value::emptyValue(); |
| ScopedValue tmp(scope); |
| |
| if (o->hasProperty(engine->id_enumerable()->toPropertyKey())) |
| attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean()); |
| |
| if (o->hasProperty(engine->id_configurable()->toPropertyKey())) |
| attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean()); |
| |
| if (o->hasProperty(engine->id_get()->toPropertyKey())) { |
| ScopedValue get(scope, o->get(engine->id_get())); |
| FunctionObject *f = get->as<FunctionObject>(); |
| if (f || get->isUndefined()) { |
| desc->value = get; |
| } else { |
| engine->throwTypeError(); |
| return; |
| } |
| attrs->setType(PropertyAttributes::Accessor); |
| } |
| |
| if (o->hasProperty(engine->id_set()->toPropertyKey())) { |
| ScopedValue set(scope, o->get(engine->id_set())); |
| FunctionObject *f = set->as<FunctionObject>(); |
| if (f || set->isUndefined()) { |
| desc->set = set; |
| } else { |
| engine->throwTypeError(); |
| return; |
| } |
| attrs->setType(PropertyAttributes::Accessor); |
| } |
| |
| if (o->hasProperty(engine->id_writable()->toPropertyKey())) { |
| if (attrs->isAccessor()) { |
| engine->throwTypeError(); |
| return; |
| } |
| attrs->setWritable((tmp = o->get(engine->id_writable()))->toBoolean()); |
| } |
| |
| if (o->hasProperty(engine->id_value()->toPropertyKey())) { |
| if (attrs->isAccessor()) { |
| engine->throwTypeError(); |
| return; |
| } |
| desc->value = o->get(engine->id_value()); |
| attrs->setType(PropertyAttributes::Data); |
| } |
| |
| if (attrs->isGeneric()) |
| desc->value = Value::emptyValue(); |
| } |
| |
| |
| ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, const Property *desc, PropertyAttributes attrs) |
| { |
| if (attrs.isEmpty()) |
| return Encode::undefined(); |
| |
| Scope scope(engine); |
| // Let obj be the result of creating a new object as if by the expression new Object() where Object |
| // is the standard built-in constructor with that name. |
| ScopedObject o(scope, engine->newObject()); |
| ScopedString s(scope); |
| ScopedValue v(scope); |
| |
| if (attrs.isData()) { |
| s = engine->newString(QStringLiteral("value")); |
| o->put(s, desc->value); |
| v = Value::fromBoolean(attrs.isWritable()); |
| s = engine->newString(QStringLiteral("writable")); |
| o->put(s, v); |
| } else { |
| v = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined(); |
| s = engine->newString(QStringLiteral("get")); |
| o->put(s, v); |
| v = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined(); |
| s = engine->newString(QStringLiteral("set")); |
| o->put(s, v); |
| } |
| v = Value::fromBoolean(attrs.isEnumerable()); |
| s = engine->newString(QStringLiteral("enumerable")); |
| o->put(s, v); |
| v = Value::fromBoolean(attrs.isConfigurable()); |
| s = engine->newString(QStringLiteral("configurable")); |
| o->put(s, v); |
| |
| return o.asReturnedValue(); |
| } |
| |
| // es6: GetOwnPropertyKeys |
| Heap::ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) |
| { |
| Scope scope(v4); |
| ScopedArrayObject array(scope, v4->newArrayObject()); |
| ScopedObject O(scope, o.toObject(v4)); |
| if (O) { |
| ObjectIterator it(scope, O, ObjectIterator::NoFlags); |
| ScopedValue name(scope); |
| while (1) { |
| name = it.nextPropertyNameAsString(); |
| if (name->isNull()) |
| break; |
| if (name->isSymbol()) |
| continue; |
| array->push_back(name); |
| } |
| } |
| return array->d(); |
| } |