Revision control
++ // Copyright (C) 2016 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
++ // Qt-Security score:significant
++
++ #include "qv4lookup_p.h"
++
++ #include <private/qqmlvaluetypewrapper_p.h>
++ #include <private/qv4functionobject_p.h>
++ #include <private/qv4identifiertable_p.h>
++ #include <private/qv4qobjectwrapper_p.h>
++ #include <private/qv4runtime_p.h>
++ #include <private/qv4stackframe_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace QV4;
++
++
++ void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
++ {
++ while (proto) {
++ auto index = proto->internalClass->findValueOrGetter(name);
++ if (index.isValid()) {
++ PropertyAttributes attrs = index.attrs;
++ protoLookup.data = proto->propertyData(index.index);
++ if (attrs.isData()) {
++ call = Call::GetterProto;
++ } else {
++ call = Call::GetterProtoAccessor;
++ }
++ return;
++ }
++ proto = proto->prototype();
++ }
++ // ### put in a getterNotFound!
++ call = Call::GetterQObjectPropertyFallback;
++ }
++
++ ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
++ {
++ return object->resolveLookupGetter(engine, this);
++ }
++
++ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ primitiveLookup.type = object.type();
++ switch (primitiveLookup.type) {
++ case Value::Undefined_Type:
++ case Value::Null_Type: {
++ Scope scope(engine);
++ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
++ const QString message = QStringLiteral("Cannot read property '%1' of %2").arg(name->toQString())
++ .arg(QLatin1String(primitiveLookup.type == Value::Undefined_Type ? "undefined" : "null"));
++ return engine->throwTypeError(message);
++ }
++ case Value::Boolean_Type:
++ primitiveLookup.proto.set(engine, engine->booleanPrototype()->d());
++ break;
++ case Value::Managed_Type: {
++ // ### Should move this over to the Object path, as strings also have an internalClass
++ Q_ASSERT(object.isStringOrSymbol());
++ primitiveLookup.proto.set(engine, static_cast<const Managed &>(object).internalClass()->prototype);
++ Q_ASSERT(primitiveLookup.proto);
++ Scope scope(engine);
++ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
++ if (object.isString() && name->equals(engine->id_length())) {
++ // special case, as the property is on the object itself
++ call = Call::GetterStringLength;
++ return stringLengthGetter(this, engine, object);
++ }
++ break;
++ }
++ case Value::Integer_Type:
++ default: // Number
++ primitiveLookup.proto.set(engine, engine->numberPrototype()->d());
++ }
++
++ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
++ protoLookup.protoId = primitiveLookup.proto->internalClass->protoId;
++ resolveProtoGetter(name, primitiveLookup.proto);
++
++ switch (call) {
++ case Call::GetterProto:
++ call = Call::GetterProtoPrimitive;
++ break;
++ case Call::GetterProtoAccessor:
++ call = Call::GetterAccessorPrimitive;
++ break;
++ default:
++ break;
++ }
++
++ return getter(engine, object);
++ }
++
++ ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ Object *o = engine->globalObject;
++ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
++ protoLookup.protoId = o->internalClass()->protoId;
++ resolveProtoGetter(name, o->d());
++
++ switch (call) {
++ case Call::GetterProto:
++ call = Call::GlobalGetterProto;
++ break;
++ case Call::GetterProtoAccessor:
++ call = Call::GlobalGetterProtoAccessor;
++ break;
++ default:
++ call = Call::GlobalGetterGeneric;
++ Scope scope(engine);
++ ScopedString n(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
++ return engine->throwReferenceError(n);
++ }
++
++ return globalGetter(engine);
++ }
++
++ ReturnedValue Lookup::getterGeneric(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ if (const Object *o = object.as<Object>())
++ return lookup->resolveGetter(engine, o);
++ return lookup->resolvePrimitiveGetter(engine, object);
++ }
++
++ static inline void setupObjectLookupTwoClasses(Lookup *lookup, const Lookup &first, const Lookup &second)
++ {
++ Heap::InternalClass *ic1 = first.objectLookup.ic;
++ const uint offset1 = first.objectLookup.offset;
++ Heap::InternalClass *ic2 = second.objectLookup.ic;
++ const uint offset2 = second.objectLookup.offset;
++ auto engine = ic1->engine;
++
++ lookup->objectLookupTwoClasses.ic.set(engine, ic1);
++ lookup->objectLookupTwoClasses.ic2.set(engine, ic2);
++ lookup->objectLookupTwoClasses.offset = offset1;
++ lookup->objectLookupTwoClasses.offset2 = offset2;
++ }
++
++ static inline void setupProtoLookupTwoClasses(Lookup *lookup, const Lookup &first, const Lookup &second)
++ {
++ const quintptr protoId1 = first.protoLookup.protoId;
++ const Value *data1 = first.protoLookup.data;
++ const quintptr protoId2 = second.protoLookup.protoId;
++ const Value *data2 = second.protoLookup.data;
++
++ lookup->protoLookupTwoClasses.protoId = protoId1;
++ lookup->protoLookupTwoClasses.protoId2 = protoId2;
++ lookup->protoLookupTwoClasses.data = data1;
++ lookup->protoLookupTwoClasses.data2 = data2;
++ }
++
++ ReturnedValue Lookup::getterTwoClasses(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ if (const Object *o = object.as<Object>()) {
++
++ // Do the resolution on a second lookup, then merge.
++ Lookup second;
++ memset(&second, 0, sizeof(Lookup));
++ second.nameIndex = lookup->nameIndex;
++ second.forCall = lookup->forCall;
++ second.call = Call::GetterGeneric;
++ const ReturnedValue result = second.resolveGetter(engine, o);
++
++ switch (lookup->call) {
++ case Call::Getter0Inline: {
++ switch (second.call) {
++ case Call::Getter0Inline:
++ setupObjectLookupTwoClasses(lookup, *lookup, second);
++ lookup->call = Call::Getter0InlineGetter0Inline;
++ return result;
++ case Call::Getter0MemberData:
++ setupObjectLookupTwoClasses(lookup, *lookup, second);
++ lookup->call = Call::Getter0InlineGetter0MemberData;
++ return result;
++ default:
++ break;
++ }
++ break;
++ }
++ case Call::Getter0MemberData:
++ switch (second.call) {
++ case Call::Getter0Inline:
++ // NB: Reversed order, so that we can use the same lookup function as above.
++ setupObjectLookupTwoClasses(lookup, second, *lookup);
++ lookup->call = Call::Getter0InlineGetter0MemberData;
++ return result;
++ case Call::Getter0MemberData:
++ setupObjectLookupTwoClasses(lookup, *lookup, second);
++ lookup->call = Call::Getter0MemberDataGetter0MemberData;
++ return result;
++ default:
++ break;
++ }
++ break;
++ case Call::GetterProto:
++ switch (second.call) {
++ case Call::GetterProto:
++ setupProtoLookupTwoClasses(lookup, *lookup, second);
++ lookup->call = Call::GetterProtoTwoClasses;
++ return result;
++ default:
++ break;
++ }
++ break;
++ case Call::GetterProtoAccessor:
++ switch (second.call) {
++ case Call::GetterProtoAccessor:
++ setupProtoLookupTwoClasses(lookup, *lookup, second);
++ lookup->call = Call::GetterProtoAccessorTwoClasses;
++ return result;
++ default:
++ break;
++ }
++ break;
++ default:
++ break;
++ }
++
++ // If any of the above options were true, the propertyCache was inactive.
++ second.releasePropertyCache();
++ }
++
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterFallback(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ QV4::Scope scope(engine);
++ QV4::ScopedObject o(scope, object.toObject(scope.engine));
++ if (!o)
++ return Encode::undefined();
++ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
++ return o->get(name);
++ }
++
++ ReturnedValue Lookup::getter0MemberData(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->objectLookup.ic == o->internalClass)
++ return o->memberData->values.data()[lookup->objectLookup.offset].asReturnedValue();
++ }
++ return getterTwoClasses(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getter0Inline(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->objectLookup.ic == o->internalClass)
++ return o->inlinePropertyDataWithOffset(lookup->objectLookup.offset)->asReturnedValue();
++ }
++ return getterTwoClasses(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterProto(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->protoLookup.protoId == o->internalClass->protoId)
++ return lookup->protoLookup.data->asReturnedValue();
++ }
++ return getterTwoClasses(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->objectLookupTwoClasses.ic == o->internalClass)
++ return o->inlinePropertyDataWithOffset(lookup->objectLookupTwoClasses.offset)->asReturnedValue();
++ if (lookup->objectLookupTwoClasses.ic2 == o->internalClass)
++ return o->inlinePropertyDataWithOffset(lookup->objectLookupTwoClasses.offset2)->asReturnedValue();
++ }
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->objectLookupTwoClasses.ic == o->internalClass)
++ return o->inlinePropertyDataWithOffset(lookup->objectLookupTwoClasses.offset)->asReturnedValue();
++ if (lookup->objectLookupTwoClasses.ic2 == o->internalClass)
++ return o->memberData->values.data()[lookup->objectLookupTwoClasses.offset2].asReturnedValue();
++ }
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->objectLookupTwoClasses.ic == o->internalClass)
++ return o->memberData->values.data()[lookup->objectLookupTwoClasses.offset].asReturnedValue();
++ if (lookup->objectLookupTwoClasses.ic2 == o->internalClass)
++ return o->memberData->values.data()[lookup->objectLookupTwoClasses.offset2].asReturnedValue();
++ }
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterProtoTwoClasses(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->protoLookupTwoClasses.protoId == o->internalClass->protoId)
++ return lookup->protoLookupTwoClasses.data->asReturnedValue();
++ if (lookup->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
++ return lookup->protoLookupTwoClasses.data2->asReturnedValue();
++ return getterFallback(lookup, engine, object);
++ }
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterAccessor(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (lookup->objectLookup.ic == o->internalClass) {
++ const Value *getter = o->propertyData(lookup->objectLookup.offset);
++ if (!getter->isFunctionObject()) // ### catch at resolve time
++ return Encode::undefined();
++
++ return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call(
++ &object, nullptr, 0));
++ }
++ }
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterProtoAccessor(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o && lookup->protoLookup.protoId == o->internalClass->protoId) {
++ const Value *getter = lookup->protoLookup.data;
++ if (!getter->isFunctionObject()) // ### catch at resolve time
++ return Encode::undefined();
++
++ return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call(
++ &object, nullptr, 0));
++ }
++ return getterTwoClasses(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ // we can safely cast to a QV4::Object here. If object is actually a string,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ const Value *getter = nullptr;
++ if (lookup->protoLookupTwoClasses.protoId == o->internalClass->protoId)
++ getter = lookup->protoLookupTwoClasses.data;
++ else if (lookup->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
++ getter = lookup->protoLookupTwoClasses.data2;
++ if (getter) {
++ if (!getter->isFunctionObject()) // ### catch at resolve time
++ return Encode::undefined();
++
++ return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call(
++ &object, nullptr, 0));
++ }
++ }
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterIndexed(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ Object *o = object.objectValue();
++ if (o) {
++ Heap::Object *ho = o->d();
++ if (ho->arrayData && ho->arrayData->type == Heap::ArrayData::Simple) {
++ Heap::SimpleArrayData *s = ho->arrayData.cast<Heap::SimpleArrayData>();
++ if (lookup->indexedLookup.index < s->values.size)
++ if (!s->data(lookup->indexedLookup.index).isEmpty())
++ return s->data(lookup->indexedLookup.index).asReturnedValue();
++ }
++ return o->get(lookup->indexedLookup.index);
++ }
++ lookup->call = Call::GetterQObjectPropertyFallback;
++ return getterFallback(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ const auto revertLookup = [lookup, engine, &object]() {
++ lookup->qobjectLookup.propertyCache->release();
++ lookup->qobjectLookup.propertyCache = nullptr;
++ lookup->call = Call::GetterGeneric;
++ return Lookup::getterGeneric(lookup, engine, object);
++ };
++
++ const QObjectWrapper::Flags flags = lookup->forCall
++ ? QObjectWrapper::AllowOverride
++ : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods);
++
++ return QObjectWrapper::lookupPropertyGetterImpl(lookup, engine, object, flags, revertLookup);
++ }
++
++ ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ const auto revertLookup = [lookup, engine, &object]() {
++ lookup->qobjectMethodLookup.propertyCache->release();
++ lookup->qobjectMethodLookup.propertyCache = nullptr;
++ lookup->call = Call::GetterGeneric;
++ return Lookup::getterGeneric(lookup, engine, object);
++ };
++
++ const QObjectWrapper::Flags flags = lookup->forCall
++ ? QObjectWrapper::AllowOverride
++ : (QObjectWrapper::AllowOverride | QObjectWrapper::AttachMethods);
++
++ return QObjectWrapper::lookupMethodGetterImpl(lookup, engine, object, flags, revertLookup);
++ }
++
++ ReturnedValue Lookup::getterFallbackMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ const auto revertLookup = [lookup, engine, &object]() {
++ lookup->call = Call::GetterGeneric;
++ return Lookup::getterGeneric(lookup, engine, object);
++ };
++
++ const Object *o = object.as<Object>();
++ if (!o || o->internalClass() != lookup->qobjectMethodLookup.ic)
++ return revertLookup();
++
++ const QObjectWrapper *This = static_cast<const QObjectWrapper *>(o);
++ QObject *qobj = This->d()->object();
++ if (QQmlData::wasDeleted(qobj))
++ return QV4::Encode::undefined();
++
++ Scope scope(engine);
++ ScopedString name(
++ scope,
++ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
++
++ QQmlPropertyData local;
++ const QQmlPropertyData *property = QQmlPropertyCache::property(
++ qobj, name, engine->callingQmlContext(), &local);
++ if (!property)
++ return Encode::undefined();
++
++ QV4::ScopedValue result(
++ scope, QObjectWrapper::getProperty(
++ engine, This->d(), qobj, property,
++ lookup->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods));
++
++ // In the general case we cannot rely on the method to exist or stay the same across calls.
++ // However, the AOT compiler can prove it in certain cases. For these, we store the method.
++ if (QObjectMethod *method = result->as<QV4::QObjectMethod>())
++ lookup->qobjectMethodLookup.method.set(engine, method->d());
++
++ return result->asReturnedValue();
++ }
++
++ ReturnedValue Lookup::getterValueType(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ const auto revertLookup = [lookup, engine, &object]() {
++ lookup->qgadgetLookup.metaObject = quintptr(0);
++ lookup->call = Call::GetterGeneric;
++ return Lookup::getterGeneric(lookup, engine, object);
++ };
++
++ // we can safely cast to a QV4::Object here. If object is something else,
++ // the internal class won't match
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (!o || o->internalClass != lookup->qgadgetLookup.ic)
++ return revertLookup();
++
++ Heap::QQmlValueTypeWrapper *valueTypeWrapper =
++ const_cast<Heap::QQmlValueTypeWrapper*>(static_cast<const Heap::QQmlValueTypeWrapper *>(o));
++ if (valueTypeWrapper->metaObject() != reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1))
++ return revertLookup();
++
++ if (valueTypeWrapper->isReference() && !valueTypeWrapper->readReference())
++ return Encode::undefined();
++
++ return QQmlValueTypeWrapper::getGadgetProperty(
++ engine, valueTypeWrapper, QMetaType(lookup->qgadgetLookup.metaType),
++ lookup->qgadgetLookup.coreIndex, lookup->qgadgetLookup.isFunction,
++ lookup->qgadgetLookup.isEnum);
++ }
++
++ ReturnedValue Lookup::primitiveGetterProto(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ if (object.type() == lookup->primitiveLookup.type && !object.isObject()) {
++ Heap::Object *o = lookup->primitiveLookup.proto;
++ if (lookup->primitiveLookup.protoId == o->internalClass->protoId)
++ return lookup->primitiveLookup.data->asReturnedValue();
++ }
++ lookup->call = Call::GetterGeneric;
++ return getterGeneric(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::primitiveGetterAccessor(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ if (object.type() == lookup->primitiveLookup.type && !object.isObject()) {
++ Heap::Object *o = lookup->primitiveLookup.proto;
++ if (lookup->primitiveLookup.protoId == o->internalClass->protoId) {
++ const Value *getter = lookup->primitiveLookup.data;
++ if (!getter->isFunctionObject()) // ### catch at resolve time
++ return Encode::undefined();
++
++ return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call(
++ &object, nullptr, 0));
++ }
++ }
++ lookup->call = Call::GetterGeneric;
++ return getterGeneric(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::stringLengthGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
++ {
++ if (const String *s = object.as<String>())
++ return Encode(s->d()->length());
++
++ lookup->call = Call::GetterGeneric;
++ return getterGeneric(lookup, engine, object);
++ }
++
++ ReturnedValue Lookup::globalGetterGeneric(Lookup *lookup, ExecutionEngine *engine)
++ {
++ return lookup->resolveGlobalGetter(engine);
++ }
++
++ ReturnedValue Lookup::globalGetterProto(Lookup *lookup, ExecutionEngine *engine)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ Heap::Object *o = engine->globalObject->d();
++ if (lookup->protoLookup.protoId == o->internalClass->protoId)
++ return lookup->protoLookup.data->asReturnedValue();
++ lookup->call = Call::GlobalGetterGeneric;
++ return globalGetterGeneric(lookup, engine);
++ }
++
++ ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *lookup, ExecutionEngine *engine)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ Heap::Object *o = engine->globalObject->d();
++ if (lookup->protoLookup.protoId == o->internalClass->protoId) {
++ const Value *getter = lookup->protoLookup.data;
++ if (!getter->isFunctionObject()) // ### catch at resolve time
++ return Encode::undefined();
++
++ return checkedResult(engine, static_cast<const FunctionObject *>(getter)->call(
++ engine->globalObject, nullptr, 0));
++ }
++
++ lookup->call = Call::GlobalGetterGeneric;
++ return globalGetterGeneric(lookup, engine);
++ }
++
++ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
++ {
++ return object->resolveLookupSetter(engine, this, value);
++ }
++
++ bool Lookup::setterGeneric(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ if (object.isObject())
++ return lookup->resolveSetter(engine, static_cast<Object *>(&object), value);
++
++ if (engine->currentStackFrame->v4Function->isStrict())
++ return false;
++
++ Scope scope(engine);
++ ScopedObject o(scope, RuntimeHelpers::convertToObject(scope.engine, object));
++ if (!o) // type error
++ return false;
++ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
++ return o->put(name, value);
++ }
++
++ bool Lookup::setterTwoClasses(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ // A precondition of this method is that lookup->objectLookup is the active variant of the union.
++ Q_ASSERT(lookup->call == Call::Setter0MemberData || lookup->call == Call::Setter0Inline);
++
++ if (object.isObject()) {
++
++ // As lookup->objectLookup is active, we can stash some members here, before resolving.
++ Heap::InternalClass *ic = lookup->objectLookup.ic;
++ const uint index = lookup->objectLookup.index;
++
++ if (!lookup->resolveSetter(engine, static_cast<Object *>(&object), value)) {
++ lookup->call = Call::SetterQObjectPropertyFallback;
++ return false;
++ }
++
++ if (lookup->call == Call::Setter0MemberData || lookup->call == Call::Setter0Inline) {
++ auto engine = ic->engine;
++ lookup->objectLookupTwoClasses.ic.set(engine, ic);
++ lookup->objectLookupTwoClasses.ic2.set(engine, ic);
++ lookup->objectLookupTwoClasses.offset = index;
++ lookup->objectLookupTwoClasses.offset2 = index;
++ lookup->call = Call::Setter0Setter0;
++ return true;
++ }
++
++ lookup->releasePropertyCache();
++ }
++
++ lookup->call = Call::SetterQObjectPropertyFallback;
++ return setterFallback(lookup, engine, object, value);
++ }
++
++ bool Lookup::setterFallback(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ QV4::Scope scope(engine);
++ QV4::ScopedObject o(scope, object.toObject(scope.engine));
++ if (!o)
++ return false;
++
++ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
++ return o->put(name, value);
++ }
++
++ bool Lookup::setter0MemberData(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o && o->internalClass == lookup->objectLookup.ic) {
++ o->memberData->values.set(engine, lookup->objectLookup.offset, value);
++ return true;
++ }
++
++ return setterTwoClasses(lookup, engine, object, value);
++ }
++
++ bool Lookup::setter0Inline(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o && o->internalClass == lookup->objectLookup.ic) {
++ o->setInlinePropertyWithOffset(engine, lookup->objectLookup.offset, value);
++ return true;
++ }
++
++ return setterTwoClasses(lookup, engine, object, value);
++ }
++
++ bool Lookup::setter0setter0(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
++ if (o) {
++ if (o->internalClass == lookup->objectLookupTwoClasses.ic) {
++ o->setProperty(engine, lookup->objectLookupTwoClasses.offset, value);
++ return true;
++ }
++ if (o->internalClass == lookup->objectLookupTwoClasses.ic2) {
++ o->setProperty(engine, lookup->objectLookupTwoClasses.offset2, value);
++ return true;
++ }
++ }
++
++ lookup->call = Call::SetterQObjectPropertyFallback;
++ return setterFallback(lookup, engine, object, value);
++ }
++
++ bool Lookup::setterInsert(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ // Otherwise we cannot trust the protoIds
++ Q_ASSERT(engine->isInitialized);
++
++ Object *o = static_cast<Object *>(object.managed());
++ if (o && o->internalClass()->protoId == lookup->insertionLookup.protoId) {
++ o->setInternalClass(lookup->insertionLookup.newClass);
++ o->d()->setProperty(engine, lookup->insertionLookup.offset, value);
++ return true;
++ }
++
++ lookup->call = Call::SetterQObjectPropertyFallback;
++ return setterFallback(lookup, engine, object, value);
++ }
++
++ bool Lookup::setterQObject(Lookup *lookup, ExecutionEngine *engine, Value &object, const Value &v)
++ {
++ // This setter just marks the presence of a qobjectlookup. It only does anything with it when
++ // running AOT-compiled code, though.
++ return setterFallback(lookup, engine, object, v);
++ }
++
++ bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value)
++ {
++ Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject());
++ bool ok;
++ uint len = value.asArrayLength(&ok);
++ if (!ok) {
++ engine->throwRangeError(value);
++ return false;
++ }
++ ok = static_cast<Object &>(object).setArrayLength(len);
++ if (!ok)
++ return false;
++ return true;
++ }
++
++ QT_END_NAMESPACE