// Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qv4reflect_p.h" #include "qv4runtimeapi_p.h" #include "qv4objectproto_p.h" #include "qv4propertykey_p.h" #include "qv4objectiterator_p.h" using namespace QV4; DEFINE_OBJECT_VTABLE(Reflect); void Heap::Reflect::init() { Object::init(); Scope scope(internalClass->engine); ScopedObject r(scope, this); r->defineDefaultProperty(QStringLiteral("apply"), QV4::Reflect::method_apply, 3); r->defineDefaultProperty(QStringLiteral("construct"), QV4::Reflect::method_construct, 2); r->defineDefaultProperty(QStringLiteral("defineProperty"), QV4::Reflect::method_defineProperty, 3); r->defineDefaultProperty(QStringLiteral("deleteProperty"), QV4::Reflect::method_deleteProperty, 2); r->defineDefaultProperty(QStringLiteral("get"), QV4::Reflect::method_get, 2); r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), QV4::Reflect::method_getOwnPropertyDescriptor, 2); r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), QV4::Reflect::method_getPrototypeOf, 1); r->defineDefaultProperty(QStringLiteral("has"), QV4::Reflect::method_has, 2); r->defineDefaultProperty(QStringLiteral("isExtensible"), QV4::Reflect::method_isExtensible, 1); r->defineDefaultProperty(QStringLiteral("ownKeys"), QV4::Reflect::method_ownKeys, 1); r->defineDefaultProperty(QStringLiteral("preventExtensions"), QV4::Reflect::method_preventExtensions, 1); r->defineDefaultProperty(QStringLiteral("set"), QV4::Reflect::method_set, 3); r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), QV4::Reflect::method_setPrototypeOf, 2); } struct CallArgs { Value *argv; int argc; }; static CallArgs createListFromArrayLike(Scope &scope, const Object *o) { int len = scope.engine->safeForAllocLength(o->getLength()); if (scope.engine->hasException) return {nullptr, 0}; Value *arguments = scope.alloc(len); for (int i = 0; i < len; ++i) { arguments[i] = o->get(i); if (scope.hasException()) return { nullptr, 0 }; } return { arguments, len }; } ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject()) return scope.engine->throwTypeError(); const Object *o = static_cast(argv + 2); CallArgs arguments = createListFromArrayLike(scope, o); if (scope.hasException()) return Encode::undefined(); return checkedResult(scope.engine, static_cast(argv[0]).call( &argv[1], arguments.argv, arguments.argc)); } ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); if (argc < 2 || !argv[1].isObject()) return scope.engine->throwTypeError(); const FunctionObject *target = argv[0].as(); const FunctionObject *newTarget = argc == 3 ? argv[2].as() : target; if (!target || !target->isConstructor() || !newTarget || !newTarget->isConstructor()) return scope.engine->throwTypeError(); const Object *o = static_cast(argv + 1); CallArgs arguments = createListFromArrayLike(scope, o); if (scope.hasException()) return Encode::undefined(); return target->callAsConstructor(arguments.argv, arguments.argc, newTarget); } ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); 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.hasException()) return QV4::Encode::undefined(); ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue()); ScopedProperty pd(scope); PropertyAttributes attrs; ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs); if (scope.hasException()) return QV4::Encode::undefined(); bool result = O->defineOwnProperty(name, pd, attrs); return Encode(result); } ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc) { ExecutionEngine *e = f->engine(); if (!argc || !argv[0].isObject()) return e->throwTypeError(); bool result = Runtime::DeleteProperty_NoThrow::call(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue()); return Encode(result); } ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); if (!argc || !argv[0].isObject()) return scope.engine->throwTypeError(); ScopedObject o(scope, static_cast(argv)); Value undef = Value::undefinedValue(); const Value *index = argc > 1 ? &argv[1] : &undef; ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine)); if (scope.hasException()) return Encode::undefined(); ScopedValue receiver(scope, argc > 2 ? argv[2] : *o); return Encode(o->get(name, receiver)); } ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { if (!argc || !argv[0].isObject()) return f->engine()->throwTypeError(); return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc); } ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (!argc || !argv[0].isObject()) return f->engine()->throwTypeError(); const Object *o = static_cast(argv); Heap::Object *p = o->getPrototypeOf(); return (p ? p->asReturnedValue() : Encode::null()); } ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); if (!argc || !argv[0].isObject()) return scope.engine->throwTypeError(); ScopedObject o(scope, static_cast(argv)); Value undef = Value::undefinedValue(); const Value *index = argc > 1 ? &argv[1] : &undef; ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine)); if (scope.hasException()) return false; return Encode(o->hasProperty(name)); } ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (!argc || !argv[0].isObject()) return f->engine()->throwTypeError(); const Object *o = static_cast(argv); return Encode(o->isExtensible()); } ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (!argc || !argv[0].isObject()) return f->engine()->throwTypeError(); Scope scope(f); if (!argc) return scope.engine->throwTypeError(); ScopedObject O(scope, argv[0].toObject(scope.engine)); if (!O) return Encode::undefined(); ScopedArrayObject keys(scope, scope.engine->newArrayObject()); ObjectIterator it(scope, O, ObjectIterator::WithSymbols); ScopedPropertyKey key(scope); ScopedValue v(scope); while (1) { key = it.next(); if (!key->isValid()) break; v = key->toStringOrSymbol(scope.engine); keys->push_back(v); } return keys->asReturnedValue(); } ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); if (!argc || !argv[0].isObject()) return scope.engine->throwTypeError(); ScopedObject o(scope, static_cast(argv)); return Encode(o->preventExtensions()); } ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc) { Scope scope(f); if (!argc || !argv[0].isObject()) return scope.engine->throwTypeError(); ScopedObject o(scope, static_cast(argv)); Value undef = Value::undefinedValue(); const Value *index = argc > 1 ? &argv[1] : &undef; const Value &val = argc > 2 ? argv[2] : undef; ScopedValue receiver(scope, argc >3 ? argv[3] : argv[0]); ScopedPropertyKey propertyKey(scope, index->toPropertyKey(scope.engine)); if (scope.hasException()) return false; bool result = o->put(propertyKey, val, receiver); return Encode(result); } ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject())) return f->engine()->throwTypeError(); Scope scope(f); ScopedObject o(scope, static_cast(argv)); const Object *proto = argv[1].isNull() ? nullptr : static_cast(argv + 1); return Encode(o->setPrototypeOf(proto)); }