diff options
Diffstat (limited to 'src/qml')
44 files changed, 957 insertions, 328 deletions
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 348ddb25d9..01ec44e250 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -852,7 +852,7 @@ QJSValue QJSValue::prototype() const ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<QV4::Object>()); if (!o) return QJSValue(); - ScopedObject p(scope, o->prototype()); + ScopedObject p(scope, o->getPrototypeOf()); if (!p) return QJSValue(NullValue); return QJSValue(o->internalClass()->engine, p.asReturnedValue()); @@ -884,7 +884,7 @@ void QJSValue::setPrototype(const QJSValue& prototype) if (!val) return; if (val->isNull()) { - o->setPrototype(nullptr); + o->setPrototypeOf(nullptr); return; } @@ -895,7 +895,7 @@ void QJSValue::setPrototype(const QJSValue& prototype) qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine"); return; } - if (!o->setPrototype(p)) + if (!o->setPrototypeOf(p)) qWarning("QJSValue::setPrototype() failed: cyclic prototype value"); } @@ -1269,8 +1269,8 @@ bool QJSValue::hasProperty(const QString &name) const if (!o) return false; - ScopedString s(scope, engine->newIdentifier(name)); - return o->hasProperty(s); + ScopedString s(scope, engine->newString(name)); + return o->hasProperty(s->toPropertyKey()); } /*! @@ -1291,7 +1291,7 @@ bool QJSValue::hasOwnProperty(const QString &name) const return false; ScopedString s(scope, engine->newIdentifier(name)); - return o->hasOwnProperty(s); + return o->getOwnProperty(s->identifier()) != Attr_Invalid; } /*! diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index a2408d7d2a..34eea36f0a 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -30,6 +30,7 @@ SOURCES += \ $$PWD/qv4numberobject.cpp \ $$PWD/qv4object.cpp \ $$PWD/qv4objectproto.cpp \ + $$PWD/qv4proxy.cpp \ $$PWD/qv4qmlcontext.cpp \ $$PWD/qv4reflect.cpp \ $$PWD/qv4regexpobject.cpp \ @@ -89,6 +90,7 @@ HEADERS += \ $$PWD/qv4numberobject_p.h \ $$PWD/qv4object_p.h \ $$PWD/qv4objectproto_p.h \ + $$PWD/qv4proxy_p.h \ $$PWD/qv4qmlcontext_p.h \ $$PWD/qv4reflect_p.h \ $$PWD/qv4regexpobject_p.h \ diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 58951d043c..3fa680bfef 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -202,19 +202,19 @@ bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) return Object::deleteIndexedProperty(m, index); } -PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) +PropertyAttributes ArgumentsObject::getOwnProperty(Managed *m, Identifier id, Property *p) { const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); - if (args->fullyCreated()) - return Object::queryIndexed(m, index); + if (!id.isArrayIndex() || args->fullyCreated()) + return Object::getOwnProperty(m, id, p); - uint numAccessors = qMin(args->d()->nFormals, args->context()->argc()); + uint index = id.asArrayIndex(); uint argCount = args->context()->argc(); if (index >= argCount) return PropertyAttributes(); - if (index >= numAccessors) - return Attr_Data; - return Attr_Accessor; + if (p) + p->value = args->context()->args()[index]; + return Attr_Data; } DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index f246f66019..2518b4642a 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -150,7 +150,7 @@ struct ArgumentsObject: Object { static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static bool putIndexed(Managed *m, uint index, const Value &value); static bool deleteIndexedProperty(Managed *m, uint index); - static PropertyAttributes queryIndexed(const Managed *m, uint index); + static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p); static qint64 getLength(const Managed *m); void fullyCreate(); diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 434f6781a8..a82628e249 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -247,7 +247,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V mappedValue = *nextValue; } - if (!a->hasOwnProperty(k)) { + if (a->getOwnProperty(Identifier::fromArrayIndex(k)) == Attr_Invalid) { a->arraySet(k, mappedValue); } else { // Don't return: we need to close the iterator. @@ -289,7 +289,7 @@ ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const V mappedValue = kValue; } - if (a->hasOwnProperty(k)) + if (a->getOwnProperty(Identifier::fromArrayIndex(k)) != Attr_Invalid) return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); a->arraySet(k, mappedValue); @@ -318,7 +318,7 @@ ReturnedValue ArrayPrototype::method_of(const FunctionObject *builtin, const Val int k = 0; while (k < argc) { - if (a->hasOwnProperty(k)) { + if (a->getOwnProperty(Identifier::fromArrayIndex(k)) != Attr_Invalid) { return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k)); } a->arraySet(k, argv[k]); diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index eb83f902db..80b95a71d4 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -66,7 +66,7 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 78cca8d525..03a56cb173 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -178,7 +178,7 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) ctx = ctx->d()->outer; } - if (activation->hasOwnProperty(name)) + if (activation->getOwnProperty(name->toPropertyKey()) != Attr_Invalid) return; ScopedProperty desc(scope); PropertyAttributes attrs(Attr_Data); @@ -209,7 +209,7 @@ bool ExecutionContext::deleteProperty(String *name) if (ctx->activation) { Scope scope(this); ScopedObject object(scope, ctx->activation); - if (object && object->hasProperty(name)) + if (object && object->hasProperty(name->toPropertyKey())) return object->deleteProperty(name); } break; @@ -236,7 +236,7 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value case Heap::ExecutionContext::Type_WithContext: { Scope scope(v4); ScopedObject w(scope, ctx->activation); - if (w->hasProperty(name)) { + if (w->hasProperty(name->toPropertyKey())) { if (!w->put(name, value)) return TypeError; return NoError; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 1073a2abab..7f26d1d16b 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -73,6 +73,7 @@ #include "qv4stringiterator_p.h" #include "qv4generatorobject_p.h" #include "qv4reflect_p.h" +#include "qv4proxy_p.h" #if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" @@ -290,6 +291,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive")); jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag")); jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables")); + jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy")); ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype()); Q_ASSERT(ic->d()->prototype); @@ -386,6 +388,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) classes[Class_ErrorProto] = ic->addMember(id_name()->identifier(), Attr_Data|Attr_NotEnumerable, &index); Q_ASSERT(index == ErrorPrototype::Index_Name); + classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable()); + jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0); jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]); @@ -398,7 +402,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d()); jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>(); - Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); + Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d()); #if QT_CONFIG(qml_sequence_object) ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); @@ -532,6 +536,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>())); globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>())); globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>())); + globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext()))); globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue()); globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN())); @@ -1838,7 +1843,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) } else if (Object *o = value->objectValue()) { // Look in the prototype chain. QV4::Scope scope(this); - QV4::ScopedObject proto(scope, o->prototype()); + QV4::ScopedObject proto(scope, o->getPrototypeOf()); while (proto) { bool canCast = false; if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) { @@ -1859,7 +1864,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) *reinterpret_cast<void* *>(data) = var.data(); return true; } - proto = proto->prototype(); + proto = proto->getPrototypeOf(); } } } else if (value->isNull() && name.endsWith('*')) { diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index b007e65c4b..abd363adcb 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -368,6 +368,7 @@ public: Symbol_toPrimitive, Symbol_toStringTag, Symbol_unscopables, + Symbol_revokableProxy, NJSSymbols }; Value *jsSymbols; @@ -425,6 +426,7 @@ public: Symbol *symbol_toPrimitive() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toPrimitive); } Symbol *symbol_toStringTag() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toStringTag); } Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); } + Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); } #ifndef V4_BOOTSTRAP QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits; diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 03ff25d5b5..7701cf37bc 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -115,6 +115,7 @@ struct Q_QML_EXPORT EngineBase { Class_ErrorObjectWithMessage, Class_ErrorProto, Class_QmlContextWrapper, + Class_ProxyObject, Class_Symbol, NClasses }; diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index a29eef513c..1c4c970c2e 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -78,9 +78,9 @@ Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Funct Scope scope(context); Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function)); ScopedObject proto(scope, scope.engine->newObject()); - proto->setPrototype(scope.engine->generatorPrototype()); + proto->setPrototypeOf(scope.engine->generatorPrototype()); g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable); - g->setPrototype(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); + g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype()))); return g->d(); } @@ -104,7 +104,7 @@ ReturnedValue GeneratorFunction::call(const FunctionObject *f, const Value *this Scope scope(gf); Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject])); - g->setPrototype(ScopedObject(scope, gf->get(scope.engine->id_prototype()))); + g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype()))); Heap::GeneratorObject *gp = g->d(); gp->stack.size = stackSize; @@ -160,7 +160,7 @@ void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor) ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable); ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable); - setPrototype(engine->iteratorPrototype()); + setPrototypeOf(engine->iteratorPrototype()); defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable); defineDefaultProperty(QStringLiteral("next"), method_next, 1); defineDefaultProperty(QStringLiteral("return"), method_return, 1); diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 47a6734eda..95f65c47c0 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -335,7 +335,7 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope) Scope s(scope); Heap::FunctionObject::init(scope, s.engine->id_eval()); ScopedFunctionObject f(s, this); - f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); + f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(1)); } ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index 4bfae14aec..08e480e8c2 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -44,6 +44,16 @@ QT_BEGIN_NAMESPACE namespace QV4 { +bool Identifier::isString() const +{ + return isValid() && asHeapObject()->internalClass->vtable->isString; +} + +bool Identifier::isSymbol() const +{ + return isValid() && !asHeapObject()->internalClass->vtable->isString && asHeapObject()->internalClass->vtable->isStringOrSymbol; +} + QString Identifier::toQString() const { if (isArrayIndex()) diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h index b167a149a2..fd4ff75974 100644 --- a/src/qml/jsruntime/qv4identifier_p.h +++ b/src/qml/jsruntime/qv4identifier_p.h @@ -73,6 +73,9 @@ struct Identifier static Identifier fromHeapObject(Heap::Base *b) { return Identifier{ reinterpret_cast<quintptr>(b) }; } Heap::Base *asHeapObject() const { return (id & 1) ? nullptr : reinterpret_cast<Heap::Base *>(id); } + bool isString() const; + bool isSymbol() const; + Q_QML_EXPORT QString toQString() const; bool operator ==(const Identifier &other) const { return id == other.id; } diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index c045ac3008..ee741e4abb 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -146,6 +146,12 @@ Heap::String *IdentifierTable::insertString(const QString &s) { uint subtype; uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + if (subtype == Heap::String::StringType_ArrayIndex) { + Heap::String *str = engine->newString(s); + str->stringHash = hash; + str->subtype = subtype; + return str; + } uint idx = hash % alloc; while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == s) diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 58f5b0051f..9321b9dd64 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -118,6 +118,9 @@ QString Managed::className() const case Type_JsonObject: s = "JSON"; break; + case Type_ProxyObject: + s = "ProxyObject"; + break; case Type_MathObject: s = "Math"; break; diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index f16bf3dc58..6983b52c83 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -196,6 +196,7 @@ public: Type_ArgumentsObject, Type_JsonObject, Type_MathObject, + Type_ProxyObject, Type_ExecutionContext, Type_InternalClass, diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index f58ff45801..de24ec8181 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -95,7 +95,7 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qt_qnan())); ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf())); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 79a63d1ee6..dff7555061 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -93,19 +93,6 @@ void Heap::Object::setUsedAsProto() internalClass.set(internalClass->engine, internalClass->asProtoClass()); } -bool Object::setPrototype(Object *proto) -{ - Heap::Object *p = proto ? proto->d() : nullptr; - Heap::Object *pp = p; - while (pp) { - if (pp == d()) - return false; - pp = pp->prototype(); - } - setInternalClass(internalClass()->changePrototype(p)); - return true; -} - ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) { if (!attrs.isAccessor()) @@ -275,48 +262,9 @@ void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttribut } } -// Section 8.12.1 -void Object::getOwnProperty(StringOrSymbol *name, PropertyAttributes *attrs, Property *p) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return getOwnProperty(idx, attrs, p); - - name->makeIdentifier(); - Identifier id = name->identifier(); - - uint member = internalClass()->find(id); - if (member < UINT_MAX) { - *attrs = internalClass()->propertyData[member]; - if (p) { - p->value = *propertyData(member); - if (attrs->isAccessor()) - p->set = *propertyData(member + SetterOffset); - } - return; - } - - if (attrs) - *attrs = Attr_Invalid; - return; -} - -void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) +void Object::setPrototypeUnchecked(const Object *p) { - if (arrayData()) { - if (arrayData()->getProperty(index, p, attrs)) - return; - } - if (isStringObject()) { - *attrs = Attr_NotConfigurable|Attr_NotWritable; - if (p) - p->value = static_cast<StringObject *>(this)->getIndex(index); - return; - } - - if (attrs) - *attrs = Attr_Invalid; - return; + setInternalClass(internalClass()->changePrototype(p ? p->d() : nullptr)); } // Section 8.12.2 @@ -366,68 +314,6 @@ PropertyIndex Object::getValueOrSetter(uint index, PropertyAttributes *attrs) return { nullptr, 0 }; } -bool Object::hasProperty(StringOrSymbol *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return hasProperty(idx); - - Scope scope(engine()); - ScopedObject o(scope, d()); - while (o) { - if (o->hasOwnProperty(name)) - return true; - - o = o->prototype(); - } - - return false; -} - -bool Object::hasProperty(uint index) const -{ - Scope scope(engine()); - ScopedObject o(scope, d()); - while (o) { - if (o->hasOwnProperty(index)) - return true; - - o = o->prototype(); - } - - return false; -} - -bool Object::hasOwnProperty(StringOrSymbol *name) const -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return hasOwnProperty(idx); - - name->makeIdentifier(); - Identifier id = name->identifier(); - - if (internalClass()->find(id) < UINT_MAX) - return true; - if (!query(name).isEmpty()) - return true; - return false; -} - -bool Object::hasOwnProperty(uint index) const -{ - if (arrayData() && !arrayData()->isEmpty(index)) - return true; - - if (isStringObject()) { - if (index < static_cast<const StringObject *>(this)->length()) - return true; - } - if (!queryIndexed(index).isEmpty()) - return true; - return false; -} - ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int) { return f->engine()->throwTypeError(); @@ -458,36 +344,6 @@ bool Object::putIndexed(Managed *m, uint index, const Value &value) return static_cast<Object *>(m)->internalPutIndexed(index, value); } -PropertyAttributes Object::query(const Managed *m, StringOrSymbol *name) -{ - uint idx = name->asArrayIndex(); - if (idx != UINT_MAX) - return queryIndexed(m, idx); - - name->makeIdentifier(); - Identifier id = name->identifier(); - - const Object *o = static_cast<const Object *>(m); - idx = o->internalClass()->find(id); - if (idx < UINT_MAX) - return o->internalClass()->propertyData[idx]; - - return Attr_Invalid; -} - -PropertyAttributes Object::queryIndexed(const Managed *m, uint index) -{ - const Object *o = static_cast<const Object *>(m); - if (o->arrayData() && !o->arrayData()->isEmpty(index)) - return o->arrayData()->attributes(index); - - if (o->isStringObject()) { - if (index < static_cast<const StringObject *>(o)->length()) - return (Attr_NotWritable|Attr_NotConfigurable); - } - return Attr_Invalid; -} - bool Object::deleteProperty(Managed *m, StringOrSymbol *name) { return static_cast<Object *>(m)->internalDeleteProperty(name); @@ -617,7 +473,7 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const return str.asReturnedValue(); } } - o = o->prototype(); + o = o->getPrototypeOf(); } if (exists) { @@ -676,13 +532,13 @@ bool Object::internalPut(StringOrSymbol *name, const Value &value) memberIndex.set(engine, value); } return true; - } else if (!prototype()) { + } else if (!getPrototypeOf()) { if (!isExtensible()) return false; } else { // clause 4 Scope scope(engine); - memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs); + memberIndex = ScopedObject(scope, getPrototypeOf())->getValueOrSetter(name, &attrs); if (!memberIndex.isNull()) { if (attrs.isAccessor()) { if (!memberIndex->as<FunctionObject>()) @@ -741,13 +597,13 @@ bool Object::internalPutIndexed(uint index, const Value &value) arrayIndex.set(engine, value); return true; - } else if (!prototype()) { + } else if (!getPrototypeOf()) { if (!isExtensible()) return false; } else { // clause 4 Scope scope(engine); - arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs); + arrayIndex = ScopedObject(scope, getPrototypeOf())->getValueOrSetter(index, &attrs); if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { if (!arrayIndex->as<FunctionObject>()) @@ -1102,6 +958,98 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) return Encode(false); } +bool Object::hasProperty(const Managed *m, Identifier id) +{ + Scope scope(m->engine()); + ScopedObject o(scope, m); + ScopedProperty p(scope); + while (o) { + if (o->getOwnProperty(id, p) != Attr_Invalid) + return true; + + o = o->getPrototypeOf(); + } + + return false; +} + +PropertyAttributes Object::getOwnProperty(Managed *m, Identifier id, Property *p) +{ + PropertyAttributes attrs; + Object *o = static_cast<Object *>(m); + if (id.isArrayIndex()) { + uint index = id.asArrayIndex(); + if (o->arrayData()) { + if (o->arrayData()->getProperty(index, p, &attrs)) + return attrs; + } + if (o->isStringObject()) { + if (index >= static_cast<const StringObject *>(m)->length()) + return Attr_Invalid; + attrs = Attr_NotConfigurable|Attr_NotWritable; + if (p) + p->value = static_cast<StringObject *>(o)->getIndex(index); + return attrs; + } + } else { + Q_ASSERT(id.asHeapObject()); + + uint member = o->internalClass()->find(id); + if (member < UINT_MAX) { + attrs = o->internalClass()->propertyData[member]; + if (p) { + p->value = *o->propertyData(member); + if (attrs.isAccessor()) + p->set = *o->propertyData(member + SetterOffset); + } + return attrs; + } + } + + return Attr_Invalid; +} + +bool Object::isExtensible(const Managed *m) +{ + return m->d()->internalClass->extensible; +} + +bool Object::preventExtensions(Managed *m) +{ + Q_ASSERT(m->isObject()); + Object *o = static_cast<Object *>(m); + o->setInternalClass(o->internalClass()->nonExtensible()); + return true; +} + +Heap::Object *Object::getPrototypeOf(const Managed *m) +{ + return m->internalClass()->prototype; +} + +bool Object::setPrototypeOf(Managed *m, const Object *proto) +{ + Q_ASSERT(m->isObject()); + Object *o = static_cast<Object *>(m); + Heap::Object *current = o->internalClass()->prototype; + Heap::Object *protod = proto ? proto->d() : nullptr; + if (current == protod) + return true; + if (!o->internalClass()->extensible) + return false; + Heap::Object *p = protod; + while (p) { + if (p == o->d()) + return false; + if (reinterpret_cast<const ObjectVTable *>(p->vtable())->getPrototypeOf != + reinterpret_cast<const ObjectVTable *>(Object::staticVTable())->getPrototypeOf) + break; + p = p->prototype(); + } + o->setInternalClass(o->internalClass()->changePrototype(protod)); + return true; +} + bool Object::setArrayLength(uint newLen) { Q_ASSERT(isArrayObject()); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 5ad67635db..6ec7452ca6 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -173,10 +173,14 @@ struct ObjectVTable ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); bool (*put)(Managed *, StringOrSymbol *name, const Value &value); bool (*putIndexed)(Managed *, uint index, const Value &value); - PropertyAttributes (*query)(const Managed *, StringOrSymbol *name); - PropertyAttributes (*queryIndexed)(const Managed *, uint index); bool (*deleteProperty)(Managed *m, StringOrSymbol *name); bool (*deleteIndexedProperty)(Managed *m, uint index); + bool (*hasProperty)(const Managed *m, Identifier id); + PropertyAttributes (*getOwnProperty)(Managed *m, Identifier id, Property *p); + bool (*isExtensible)(const Managed *); + bool (*preventExtensions)(Managed *); + Heap::Object *(*getPrototypeOf)(const Managed *); + bool (*setPrototypeOf)(Managed *, const Object *); qint64 (*getLength)(const Managed *m); void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var); @@ -192,10 +196,14 @@ const QV4::ObjectVTable classname::static_vtbl = \ getIndexed, \ put, \ putIndexed, \ - query, \ - queryIndexed, \ deleteProperty, \ deleteIndexedProperty, \ + hasProperty, \ + getOwnProperty, \ + isExtensible, \ + preventExtensions, \ + getPrototypeOf, \ + setPrototypeOf, \ getLength, \ advanceIterator, \ instanceOf \ @@ -240,20 +248,17 @@ struct Q_QML_EXPORT Object: Managed { void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); } const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); } - Heap::Object *prototype() const { return d()->prototype(); } - bool setPrototype(Object *proto); - void getOwnProperty(StringOrSymbol *name, PropertyAttributes *attrs, Property *p = nullptr); - void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = nullptr); + PropertyAttributes getOwnProperty(Identifier id, Property *p = nullptr) { + return vtable()->getOwnProperty(this, id, p); + } PropertyIndex getValueOrSetter(StringOrSymbol *name, PropertyAttributes *attrs); PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs); - bool hasProperty(StringOrSymbol *name) const; - bool hasProperty(uint index) const; - - bool hasOwnProperty(StringOrSymbol *name) const; - bool hasOwnProperty(uint index) const; + bool hasProperty(Identifier id) const { + return vtable()->hasProperty(this, id); + } bool __defineOwnProperty__(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs); bool __defineOwnProperty__(ExecutionEngine *engine, StringOrSymbol *name, const Property *p, PropertyAttributes attrs); @@ -302,7 +307,11 @@ struct Q_QML_EXPORT Object: Managed { } void insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes); - bool isExtensible() const { return d()->internalClass->extensible; } + bool isExtensible() const { return vtable()->isExtensible(this); } + bool preventExtensions() { return vtable()->preventExtensions(this); } + Heap::Object *getPrototypeOf() const { return vtable()->getPrototypeOf(this); } + bool setPrototypeOf(const Object *p) { return vtable()->setPrototypeOf(this, p); } + void setPrototypeUnchecked(const Object *p); // Array handling @@ -362,7 +371,7 @@ public: Scope scope(engine()); ScopedObject p(scope, this); - while ((p = p->prototype())) + while ((p = p->getPrototypeOf())) if (p->arrayData()) return true; @@ -418,10 +427,6 @@ public: return ret; } - PropertyAttributes query(StringOrSymbol *name) const - { return vtable()->query(this, name); } - PropertyAttributes queryIndexed(uint index) const - { return vtable()->queryIndexed(this, index); } bool deleteProperty(StringOrSymbol *name) { return vtable()->deleteProperty(this, name); } bool deleteIndexedProperty(uint index) @@ -439,10 +444,14 @@ protected: static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static bool put(Managed *m, StringOrSymbol *name, const Value &value); static bool putIndexed(Managed *m, uint index, const Value &value); - static PropertyAttributes query(const Managed *m, StringOrSymbol *name); - static PropertyAttributes queryIndexed(const Managed *m, uint index); static bool deleteProperty(Managed *m, StringOrSymbol *name); static bool deleteIndexedProperty(Managed *m, uint index); + static bool hasProperty(const Managed *m, Identifier id); + static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p); + static bool isExtensible(const Managed *m); + static bool preventExtensions(Managed *); + static Heap::Object *getPrototypeOf(const Managed *); + static bool setPrototypeOf(Managed *, const Object *); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static qint64 getLength(const Managed *m); static ReturnedValue instanceOf(const Object *typeObject, const Value &var); diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 1290a2c1b2..fc4a06747f 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -104,12 +104,12 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib n = *name; bool shadowed = false; while (o->d() != current->heapObject()) { - if ((!!n && o->hasOwnProperty(n)) || - (*index != UINT_MAX && o->hasOwnProperty(*index))) { + Identifier id = n ? (n->makeIdentifier(), n->identifier()) : Identifier::fromArrayIndex(*index); + if ((id.isValid() ||id.isArrayIndex()) && o->getOwnProperty(id) != Attr_Invalid) { shadowed = true; break; } - o = o->prototype(); + o = o->getPrototypeOf(); } if (shadowed) continue; @@ -118,7 +118,7 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib } if (flags & WithProtoChain) - current->setM(co->prototype()); + current->setM(co->getPrototypeOf()); else current->setM(nullptr); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index a61b5d5136..9d52ab1980 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -71,7 +71,7 @@ ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value ScopedObject obj(scope, scope.engine->newObject()); ScopedObject proto(scope, ctor->get(scope.engine->id_prototype())); if (!!proto) - obj->setPrototype(proto); + obj->setPrototypeOf(proto); return obj.asReturnedValue(); } else { return argv[0].toObject(v4)->asReturnedValue(); @@ -136,7 +136,7 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co if (scope.engine->hasException) return QV4::Encode::undefined(); - ScopedObject p(scope, o->prototype()); + ScopedObject p(scope, o->getPrototypeOf()); return (!!p ? p->asReturnedValue() : Encode::null()); } @@ -167,9 +167,8 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj if (scope.engine->hasException) return QV4::Encode::undefined(); - PropertyAttributes attrs; ScopedProperty desc(scope); - O->getOwnProperty(name, &attrs, desc); + PropertyAttributes attrs = O->getOwnProperty(name->toPropertyKey(), desc); return fromPropertyDescriptor(scope.engine, desc, attrs); } @@ -237,9 +236,8 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu for (quint32 i = 0; i < length; ++i) { nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine); - PropertyAttributes attrs; ScopedProperty prop(scope); - from->getOwnProperty(nextKey, &attrs, prop); + PropertyAttributes attrs = from->getOwnProperty(nextKey->toPropertyKey(), prop); if (attrs == PropertyFlag::Attr_Invalid) continue; @@ -266,7 +264,7 @@ ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, cons ScopedObject O(scope, argv[0]); ScopedObject newObject(scope, scope.engine->newObject()); - newObject->setPrototype(O); + newObject->setPrototypeOf(O); if (argc > 1 && !argv[1].isUndefined()) { @@ -403,7 +401,7 @@ ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b, if (!o) return argv[0].asReturnedValue(); - o->setInternalClass(o->internalClass()->nonExtensible()); + o->preventExtensions(); return o.asReturnedValue(); } @@ -518,17 +516,10 @@ ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, co return argv[0].asReturnedValue(); ScopedObject o(scope, argv[0]); - ScopedObject p(scope, argv[1]); - Q_ASSERT(!!o); - - if (o->prototype() != p->d()) { - bool ok = false; - if (o->isExtensible()) { - ok = o->setPrototype(p); - } - if (!ok) - return scope.engine->throwTypeError(QStringLiteral("Object.setPrototypeOf: Could not change prototype")); - } + 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(); } @@ -592,9 +583,7 @@ ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, co ScopedObject O(scope, thisObject->toObject(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - bool r = O->hasOwnProperty(P); - if (!r) - r = !O->query(P).isEmpty(); + bool r = O->getOwnProperty(P->toPropertyKey()) != Attr_Invalid; return Encode(r); } @@ -608,11 +597,11 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con ScopedObject O(scope, thisObject->toObject(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - ScopedObject proto(scope, V->prototype()); + ScopedObject proto(scope, V->getPrototypeOf()); while (proto) { if (O->d() == proto->d()) return Encode(true); - proto = proto->prototype(); + proto = proto->getPrototypeOf(); } return Encode(false); } @@ -627,8 +616,7 @@ ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject ScopedObject o(scope, thisObject->toObject(scope.engine)); if (scope.engine->hasException) return QV4::Encode::undefined(); - PropertyAttributes attrs; - o->getOwnProperty(p, &attrs); + PropertyAttributes attrs = o->getOwnProperty(p->toPropertyKey()); return Encode(attrs.isEnumerable()); } @@ -699,32 +687,21 @@ ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const V if (!o) THROW_TYPE_ERROR(); - return Encode(o->prototype()); + 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) + if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull())) THROW_TYPE_ERROR(); - if (argv[0].isNull()) { - o->setPrototype(nullptr); - RETURN_UNDEFINED(); - } - - ScopedObject p(scope, argv[0]); - bool ok = false; - if (!!p) { - if (o->prototype() == p->d()) { - ok = true; - } else if (o->isExtensible()) { - ok = o->setPrototype(p); - } - } + const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv); + bool ok = o->setPrototypeOf(p); if (!ok) - return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); + return scope.engine->throwTypeError(QStringLiteral("Could not change prototype.")); + return Encode::undefined(); RETURN_UNDEFINED(); } @@ -742,13 +719,13 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->set = Primitive::emptyValue(); ScopedValue tmp(scope); - if (o->hasProperty(engine->id_enumerable())) + if (o->hasProperty(engine->id_enumerable()->toPropertyKey())) attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean()); - if (o->hasProperty(engine->id_configurable())) + if (o->hasProperty(engine->id_configurable()->toPropertyKey())) attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean()); - if (o->hasProperty(engine->id_get())) { + if (o->hasProperty(engine->id_get()->toPropertyKey())) { ScopedValue get(scope, o->get(engine->id_get())); FunctionObject *f = get->as<FunctionObject>(); if (f || get->isUndefined()) { @@ -760,7 +737,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_set())) { + if (o->hasProperty(engine->id_set()->toPropertyKey())) { ScopedValue set(scope, o->get(engine->id_set())); FunctionObject *f = set->as<FunctionObject>(); if (f || set->isUndefined()) { @@ -772,7 +749,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value attrs->setType(PropertyAttributes::Accessor); } - if (o->hasProperty(engine->id_writable())) { + if (o->hasProperty(engine->id_writable()->toPropertyKey())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; @@ -782,7 +759,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value desc->value = Primitive::undefinedValue(); } - if (o->hasProperty(engine->id_value())) { + if (o->hasProperty(engine->id_value()->toPropertyKey())) { if (attrs->isAccessor()) { engine->throwTypeError(); return; diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp new file mode 100644 index 0000000000..1e0c452430 --- /dev/null +++ b/src/qml/jsruntime/qv4proxy.cpp @@ -0,0 +1,520 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "qv4proxy_p.h" +#include "qv4symbol_p.h" +#include "qv4jscall_p.h" +#include "qv4objectproto_p.h" + +using namespace QV4; + +DEFINE_OBJECT_VTABLE(ProxyObject); + +void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler) +{ + Object::init(); + ExecutionEngine *e = internalClass->engine; + this->target.set(e, target->d()); + this->handler.set(e, handler->d()); +} + +ReturnedValue ProxyObject::get(const Managed *m, StringOrSymbol *name, bool *hasProperty) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_get())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->get(name, hasProperty); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + if (hasProperty) + *hasProperty = true; + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = name; + cdata.args[2] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(name->toPropertyKey(), targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!trapResult->sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->value.isUndefined()) { + if (!trapResult->isUndefined()) + return scope.engine->throwTypeError(); + } + } + return trapResult->asReturnedValue(); +} + +ReturnedValue ProxyObject::getIndexed(const Managed *m, uint index, bool *hasProperty) +{ + Scope scope(m); + ScopedString name(scope, Primitive::fromUInt32(index).toString(scope.engine)); + return get(m, name, hasProperty); +} + +bool ProxyObject::put(Managed *m, StringOrSymbol *name, const Value &value) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedValue trap(scope, handler->get(scope.engine->id_set())); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->put(name, value); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 4, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = name; + cdata.args[2] = value; + cdata.args[3] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(name->toPropertyKey(), targetDesc); + if (attributes != Attr_Invalid && !attributes.isConfigurable()) { + if (attributes.isData() && !attributes.isWritable()) { + if (!value.sameValue(targetDesc->value)) + return scope.engine->throwTypeError(); + } + if (attributes.isAccessor() && targetDesc->set.isUndefined()) + return scope.engine->throwTypeError(); + } + return true; +} + +bool ProxyObject::putIndexed(Managed *m, uint index, const Value &value) +{ + Scope scope(m); + ScopedString name(scope, Primitive::fromUInt32(index).toString(scope.engine)); + return put(m, name, value); +} + +bool ProxyObject::deleteProperty(Managed *m, StringOrSymbol *name) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("deleteProperty"))); + ScopedValue trap(scope, handler->get(deleteProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->deleteProperty(name); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 3, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = name; + cdata.args[2] = o->d(); // ### fix receiver handling + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->toBoolean()) + return false; + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(name->toPropertyKey(), targetDesc); + if (attributes == Attr_Invalid) + return true; + if (!attributes.isConfigurable()) + return scope.engine->throwTypeError(); + return true; +} + +bool ProxyObject::deleteIndexedProperty(Managed *m, uint index) +{ + Scope scope(m); + ScopedString name(scope, Primitive::fromUInt32(index).toString(scope.engine)); + return deleteProperty(m, name); +} + +bool ProxyObject::hasProperty(const Managed *m, Identifier id) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("has"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->hasProperty(m, id); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asHeapObject(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) { + ScopedProperty targetDesc(scope); + PropertyAttributes attributes = target->getOwnProperty(id, targetDesc); + if (attributes != Attr_Invalid) { + if (!attributes.isConfigurable() || !target->isExtensible()) + return scope.engine->throwTypeError(); + } + } + return result; +} + +PropertyAttributes ProxyObject::getOwnProperty(Managed *m, Identifier id, Property *p) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("getOwnPropertyDescriptor"))); + ScopedValue trap(scope, handler->get(deleteProp)); + if (scope.hasException()) + return Attr_Invalid; + if (trap->isNullOrUndefined()) + return target->getOwnProperty(id, p); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asHeapObject(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->isObject() && !trapResult->isUndefined()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + + ScopedProperty targetDesc(scope); + PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc); + if (trapResult->isUndefined()) { + p->value = Encode::undefined(); + if (targetAttributes == Attr_Invalid) { + p->value = Encode::undefined(); + return Attr_Invalid; + } + if (!targetAttributes.isConfigurable() || !target->isExtensible()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + return Attr_Invalid; + } + + //bool extensibleTarget = target->isExtensible(); + ScopedProperty resultDesc(scope); + PropertyAttributes resultAttributes; + ObjectPrototype::toPropertyDescriptor(scope.engine, trapResult, resultDesc, &resultAttributes); + resultDesc->fullyPopulated(&resultAttributes); + + // ### + //Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc). + //If valid is false, throw a TypeError exception. + + if (!resultAttributes.isConfigurable()) { + if (targetAttributes == Attr_Invalid || !targetAttributes.isConfigurable()) { + scope.engine->throwTypeError(); + return Attr_Invalid; + } + } + + p->value = resultDesc->value; + p->set = resultDesc->set; + return resultAttributes; +} + +bool ProxyObject::isExtensible(const Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("isExtensible"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->isExtensible(); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (result != target->isExtensible()) { + scope.engine->throwTypeError(); + return false; + } + return result; +} + +bool ProxyObject::preventExtensions(Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("preventExtensions"))); + ScopedValue trap(scope, handler->get(hasProp)); + if (scope.hasException()) + return Encode::undefined(); + if (trap->isNullOrUndefined()) + return target->preventExtensions(); + if (!trap->isFunctionObject()) + return scope.engine->throwTypeError(); + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (result && target->isExtensible()) { + scope.engine->throwTypeError(); + return false; + } + return result; +} + +Heap::Object *ProxyObject::getPrototypeOf(const Managed *m) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return nullptr; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("getPrototypeOf"))); + ScopedValue trap(scope, handler->get(name)); + if (scope.hasException()) + return nullptr; + if (trap->isNullOrUndefined()) + return target->getPrototypeOf(); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return nullptr; + } + + JSCallData cdata(scope, 1, nullptr, handler); + cdata.args[0] = target; + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + if (!trapResult->isNull() && !trapResult->isObject()) { + scope.engine->throwTypeError(); + return nullptr; + } + Heap::Object *proto = trapResult->isNull() ? nullptr : static_cast<Heap::Object *>(trapResult->heapObject()); + if (!target->isExtensible()) { + Heap::Object *targetProto = target->getPrototypeOf(); + if (proto != targetProto) { + scope.engine->throwTypeError(); + return nullptr; + } + } + return proto; +} + +bool ProxyObject::setPrototypeOf(Managed *m, const Object *p) +{ + Scope scope(m); + const ProxyObject *o = static_cast<const ProxyObject *>(m); + if (!o->d()->handler) { + scope.engine->throwTypeError(); + return false; + } + + ScopedObject target(scope, o->d()->target); + Q_ASSERT(target); + ScopedObject handler(scope, o->d()->handler); + ScopedString name(scope, scope.engine->newString(QStringLiteral("setPrototypeOf"))); + ScopedValue trap(scope, handler->get(name)); + if (scope.hasException()) + return false; + if (trap->isNullOrUndefined()) + return target->setPrototypeOf(p); + if (!trap->isFunctionObject()) { + scope.engine->throwTypeError(); + return false; + } + + JSCallData cdata(scope, 2, nullptr, handler); + cdata.args[0] = target; + cdata.args[1] = p ? p->asReturnedValue() : Encode::null(); + + ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata)); + bool result = trapResult->toBoolean(); + if (!result) + return false; + if (!target->isExtensible()) { + Heap::Object *targetProto = target->getPrototypeOf(); + if (p->d() != targetProto) { + scope.engine->throwTypeError(); + return false; + } + } + return true; +} + +//ReturnedValue ProxyObject::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +//{ + +//} + +//ReturnedValue ProxyObject::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +//{ + +//} + +DEFINE_OBJECT_VTABLE(Proxy); + +void Heap::Proxy::init(QV4::ExecutionContext *ctx) +{ + Heap::FunctionObject::init(ctx, QStringLiteral("Proxy")); + + Scope scope(ctx); + Scoped<QV4::Proxy> ctor(scope, this); + ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2); + ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Primitive::fromInt32(2)); +} + +ReturnedValue Proxy::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) +{ + Scope scope(f); + if (argc < 2 || !argv[0].isObject() || !argv[1].isObject()) + return scope.engine->throwTypeError(); + + const Object *target = static_cast<const Object *>(argv); + const Object *handler = static_cast<const Object *>(argv + 1); + if (const ProxyObject *ptarget = target->as<ProxyObject>()) + if (!ptarget->d()->handler) + return scope.engine->throwTypeError(); + if (const ProxyObject *phandler = handler->as<ProxyObject>()) + if (!phandler->d()->handler) + return scope.engine->throwTypeError(); + + ScopedObject o(scope, scope.engine->memoryManager->allocate<ProxyObject>(target, handler)); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::call(const FunctionObject *f, const Value *, const Value *, int) +{ + return f->engine()->throwTypeError(); +} + +ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, const Value *argv, int argc) +{ + Scope scope(f); + ScopedObject proxy(scope, Proxy::callAsConstructor(f, argv, argc)); + if (scope.hasException()) + return Encode::undefined(); + + ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke"))); + ScopedFunctionObject revoker(scope, createBuiltinFunction(scope.engine, revoke, method_revoke, 0)); + revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy); + + ScopedObject o(scope, scope.engine->newObject()); + ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy"))); + o->defineDefaultProperty(p, proxy); + o->defineDefaultProperty(revoke, revoker); + return o->asReturnedValue(); +} + +ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int) +{ + Scope scope(f); + Scoped<ProxyObject> proxy(scope, f->get(scope.engine->symbol_revokableProxy())); + Q_ASSERT(proxy); + + proxy->d()->target.set(scope.engine, nullptr); + proxy->d()->handler.set(scope.engine, nullptr); + return Encode::undefined(); +} diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h new file mode 100644 index 0000000000..cbabc7e5d9 --- /dev/null +++ b/src/qml/jsruntime/qv4proxy_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ +#ifndef QV4PROXY_P_H +#define QV4PROXY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4object_p.h" +#include "qv4functionobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +namespace Heap { + +#define ProxyObjectMembers(class, Member) \ + Member(class, Pointer, Object *, target) \ + Member(class, Pointer, Object *, handler) + +DECLARE_HEAP_OBJECT(ProxyObject, Object) { + DECLARE_MARKOBJECTS(ProxyObject) + + void init(const QV4::Object *target, const QV4::Object *handler); +}; + +#define ProxyMembers(class, Member) \ + Member(class, Pointer, Symbol *, revokableProxySymbol) \ + +DECLARE_HEAP_OBJECT(Proxy, FunctionObject) { + DECLARE_MARKOBJECTS(Proxy) + + void init(QV4::ExecutionContext *ctx); +}; + +} + +struct ProxyObject: Object { + V4_OBJECT2(ProxyObject, Object) + Q_MANAGED_TYPE(ProxyObject) + V4_INTERNALCLASS(ProxyObject) + + static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); + static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); + static bool put(Managed *m, StringOrSymbol *name, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); + static bool deleteProperty(Managed *m, StringOrSymbol *name); + static bool deleteIndexedProperty(Managed *m, uint index); + static bool hasProperty(const Managed *m, Identifier id); + static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p); + static bool isExtensible(const Managed *m); + static bool preventExtensions(Managed *); + static Heap::Object *getPrototypeOf(const Managed *); + static bool setPrototypeOf(Managed *, const Object *); + + // those might require a second proxy object that derives from FunctionObject... +// static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); +// static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); +}; + +struct Proxy : FunctionObject +{ + V4_OBJECT2(Proxy, FunctionObject) + + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revocable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + + static ReturnedValue method_revoke(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); +}; + +} + +QT_END_NAMESPACE + +#endif // QV4ECMAOBJECTS_P_H diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index e17ce55f7b..94d645ac2b 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -733,25 +733,29 @@ bool QObjectWrapper::put(Managed *m, StringOrSymbol *n, const Value &value) return true; } -PropertyAttributes QObjectWrapper::query(const Managed *m, StringOrSymbol *name) +PropertyAttributes QObjectWrapper::getOwnProperty(Managed *m, Identifier id, Property *p) { - if (name->isSymbol()) - return QV4::Object::query(m, name); - String *n = static_cast<String *>(name); - - const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m); - const QObject *thatObject = that->d()->object(); - if (QQmlData::wasDeleted(thatObject)) - return QV4::Object::query(m, name); + if (id.isString()) { + QObjectWrapper *that = static_cast<QObjectWrapper*>(m); + const QObject *thatObject = that->d()->object(); + if (!QQmlData::wasDeleted(thatObject)) { + Scope scope(m); + ScopedString n(scope, id.asHeapObject()); + QQmlContextData *qmlContext = scope.engine->callingQmlContext(); + QQmlPropertyData local; + if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local) + || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) { + if (p) { + // ### probably not the fastest implementation + bool hasProperty; + p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true); + } + return QV4::Attr_Data; + } + } + } - ExecutionEngine *engine = that->engine(); - QQmlContextData *qmlContext = engine->callingQmlContext(); - QQmlPropertyData local; - if (that->findProperty(engine, qmlContext, n, IgnoreRevision, &local) - || n->equals(engine->id_destroy()) || n->equals(engine->id_toString())) - return QV4::Attr_Data; - else - return QV4::Object::query(m, name); + return QV4::Object::getOwnProperty(m, id, p); } void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) @@ -2089,7 +2093,7 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) } Scoped<QMetaObjectWrapper> metaObject(scope, this); object->defineDefaultProperty(v4->id_constructor(), metaObject); - object->setPrototype(const_cast<QMetaObjectWrapper*>(this)); + object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this)); return object.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 56e20adbfa..12272c2925 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -195,7 +195,7 @@ protected: static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); static bool put(Managed *m, StringOrSymbol *name, const Value &value); - static PropertyAttributes query(const Managed *, StringOrSymbol *name); + static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp index 69baecd337..9b4a5a2375 100644 --- a/src/qml/jsruntime/qv4reflect.cpp +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -175,12 +175,14 @@ ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc); } -ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) { if (!argc || !argv[0].isObject()) return f->engine()->throwTypeError(); - return ObjectPrototype::method_getPrototypeOf(f, thisObject, argv, argc); + const Object *o = static_cast<const Object *>(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) @@ -232,8 +234,7 @@ ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const V return scope.engine->throwTypeError(); ScopedObject o(scope, static_cast<const Object *>(argv)); - o->setInternalClass(o->internalClass()->nonExtensible()); - return Encode(true); + return Encode(o->preventExtensions()); } ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc) @@ -261,10 +262,13 @@ ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const return Encode(result); } -ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) { - if (argc < 2 || !argv[0].isObject()) + if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject())) return f->engine()->throwTypeError(); - return ObjectPrototype::method_setPrototypeOf(f, thisObject, argv, argc); + Scope scope(f); + ScopedObject o(scope, static_cast<const Object *>(argv)); + const Object *proto = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1); + return Encode(o->setPrototypeOf(proto)); } diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h index 73d257e006..d480e1d914 100644 --- a/src/qml/jsruntime/qv4reflect_p.h +++ b/src/qml/jsruntime/qv4reflect_p.h @@ -73,7 +73,7 @@ struct Reflect : Object { static ReturnedValue method_deleteProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *, const Value *argv, int argc); static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_ownKeys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 8759a72074..a5249fcc7e 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -374,7 +374,7 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left ScopedStringOrSymbol s(scope, left.toPropertyKey(engine)); if (scope.hasException()) return Encode::undefined(); - bool r = ro->hasProperty(s); + bool r = ro->hasProperty(s->toPropertyKey()); return Encode(r); } diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 27c73a2b77..cfd2d0a5b2 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -250,6 +250,14 @@ void Heap::StringOrSymbol::createHashValue() const stringHash = QV4::String::calculateHashValue(ch, end, &subtype); } +Identifier StringOrSymbol::toPropertyKey() const { + uint index = asArrayIndex(); + if (index < UINT_MAX) + return Identifier::fromArrayIndex(index); + makeIdentifier(); + return identifier(); +} + uint String::getLength(const Managed *m) { return static_cast<const String *>(m)->d()->length(); diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 0deb542ea2..2e3ca977bf 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -178,6 +178,8 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed { uint asArrayIndex() const; + Identifier toPropertyKey() const; + inline QString toQString() const { return d()->toQString(); } diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 3639edac17..b1ae5a1ce5 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -118,9 +118,8 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, while (it->arrayIndex < slen) { *index = it->arrayIndex; ++it->arrayIndex; - PropertyAttributes a; Property pd; - s->getOwnProperty(*index, &a, &pd); + PropertyAttributes a = s->getOwnProperty(Identifier::fromArrayIndex(*index), &pd); if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { *attrs = a; p->copy(&pd, a); @@ -220,7 +219,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) setProperty(scope.engine, Heap::StringObject::LengthPropertyIndex, Primitive::fromInt32(0)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), StringCtor::method_fromCharCode, 1); ctor->defineDefaultProperty(QStringLiteral("fromCodePoint"), StringCtor::method_fromCodePoint, 1); diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 3eadfe04a6..86aa18c3a4 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -410,9 +410,9 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3)); ctor->defineReadonlyProperty(engine->id_prototype(), *this); ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); - ctor->setPrototype(engine->intrinsicTypedArrayCtor()); + ctor->setPrototypeOf(engine->intrinsicTypedArrayCtor()); - setPrototype(engine->intrinsicTypedArrayPrototype()); + setPrototypeOf(engine->intrinsicTypedArrayPrototype()); defineDefaultProperty(engine->id_constructor(), (o = ctor)); defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement)); } diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 96f6c6aed6..9b43ea0531 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1409,7 +1409,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode)); QV4::ScopedObject p(scope, e->incubationProto.value()); - r->setPrototype(p); + r->setPrototypeOf(p); if (!valuemap->isUndefined()) r->d()->valuemap.set(scope.engine, valuemap); diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index c5a85dd4d3..42c72e0447 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -828,7 +828,7 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale) QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocate<QQmlLocaleData>()); *wrapper->d()->locale = locale; QV4::ScopedObject p(scope, d->prototype.value()); - wrapper->setPrototype(p); + wrapper->setPrototypeOf(p); return wrapper.asReturnedValue(); } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index dd027818cd..69a8ff034a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1873,7 +1873,7 @@ int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *q if (!module) return -1; - QQmlType type = module->type(QHashedStringRef(qmlName), versionMinor); + QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(qmlName)), versionMinor); if (!type.isValid()) return -1; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 7270cffb00..144d077c44 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -350,15 +350,18 @@ bool QQmlTypeWrapper::put(Managed *m, StringOrSymbol *n, const Value &value) return false; } -PropertyAttributes QQmlTypeWrapper::query(const Managed *m, StringOrSymbol *name) +PropertyAttributes QQmlTypeWrapper::getOwnProperty(Managed *m, Identifier id, Property *p) { - if (name->isSymbol()) - return Object::query(m, name); - String *n = static_cast<String *>(name); - // ### Implement more efficiently. - bool hasProperty = false; - static_cast<Object *>(const_cast<Managed*>(m))->get(n, &hasProperty); - return hasProperty ? Attr_Data : Attr_Invalid; + if (id.isString()) { + Scope scope(m); + ScopedString n(scope, id.asHeapObject()); + // ### Implement more efficiently. + bool hasProperty = false; + static_cast<Object *>(m)->get(n, &hasProperty); + return hasProperty ? Attr_Data : Attr_Invalid; + } + + return QV4::Object::getOwnProperty(m, id, p); } bool QQmlTypeWrapper::isEqualTo(Managed *a, Managed *b) diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 30bbc8d179..b17d0e0868 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -114,7 +114,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); static bool put(Managed *m, StringOrSymbol *name, const Value &value); - static PropertyAttributes query(const Managed *, StringOrSymbol *name); + static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p); static bool isEqualTo(Managed *that, Managed *o); static ReturnedValue instanceOf(const Object *typeObject, const Value &var); }; diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 7e0cf64bed..aa716b8a73 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -241,17 +241,17 @@ bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other) return false; } -PropertyAttributes QQmlValueTypeWrapper::query(const Managed *m, StringOrSymbol *name) +PropertyAttributes QQmlValueTypeWrapper::getOwnProperty(Managed *m, Identifier id, Property *p) { - if (name->isSymbol()) - return Object::query(m, name); - - String *n = static_cast<String *>(name); - Q_ASSERT(m->as<const QQmlValueTypeWrapper>()); - const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); + if (id.isString()) { + Scope scope(m); + ScopedString n(scope, id.asHeapObject()); + const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m); + QQmlPropertyData *result = r->d()->propertyCache()->property(n.getPointer(), nullptr, nullptr); + return result ? Attr_Data : Attr_Invalid; + } - QQmlPropertyData *result = r->d()->propertyCache()->property(n, nullptr, nullptr); - return result ? Attr_Data : Attr_Invalid; + return QV4::Object::getOwnProperty(m, id, p); } void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 5a684bfb4d..12d7114d2f 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -109,7 +109,7 @@ public: static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty); static bool put(Managed *m, StringOrSymbol *name, const Value &value); static bool isEqualTo(Managed *m, Managed *other); - static PropertyAttributes query(const Managed *, StringOrSymbol *name); + static PropertyAttributes getOwnProperty(Managed *m, Identifier id, Property *p); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 41531c1df3..a8faac4b0d 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -611,7 +611,7 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data) switch (data->type) { case NodeImpl::Attr: - instance->setPrototype((p = Attr::prototype(v4))); + instance->setPrototypeUnchecked((p = Attr::prototype(v4))); break; case NodeImpl::Comment: case NodeImpl::Document: @@ -623,13 +623,13 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data) case NodeImpl::ProcessingInstruction: return Encode::undefined(); case NodeImpl::CDATA: - instance->setPrototype((p = CDATA::prototype(v4))); + instance->setPrototypeUnchecked((p = CDATA::prototype(v4))); break; case NodeImpl::Text: - instance->setPrototype((p = Text::prototype(v4))); + instance->setPrototypeUnchecked((p = Text::prototype(v4))); break; case NodeImpl::Element: - instance->setPrototype((p = Element::prototype(v4))); + instance->setPrototypeUnchecked((p = Element::prototype(v4))); break; } @@ -643,7 +643,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine) Scope scope(engine); ScopedObject p(scope, engine->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(engine))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine))); p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr); d->elementPrototype.set(engine, p); engine->v8Engine->freezeObject(p); @@ -658,7 +658,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine) Scope scope(engine); ScopedObject p(scope, engine->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(engine))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine))); p->defineAccessorProperty(QStringLiteral("name"), method_name, nullptr); p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr); p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr); @@ -715,7 +715,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(v4))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4))); p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr); p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr); d->characterDataPrototype.set(v4, p); @@ -751,7 +751,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = CharacterData::prototype(v4))); + p->setPrototypeUnchecked((pp = CharacterData::prototype(v4))); p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr); p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr); d->textPrototype.set(v4, p); @@ -768,7 +768,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = Text::prototype(v4))); + p->setPrototypeUnchecked((pp = Text::prototype(v4))); d->cdataPrototype.set(v4, p); v4->v8Engine->freezeObject(p); } @@ -782,7 +782,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4) Scope scope(v4); ScopedObject p(scope, v4->newObject()); ScopedObject pp(scope); - p->setPrototype((pp = NodePrototype::getProto(v4))); + p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4))); p->defineAccessorProperty(QStringLiteral("xmlVersion"), method_xmlVersion, nullptr); p->defineAccessorProperty(QStringLiteral("xmlEncoding"), method_xmlEncoding, nullptr); p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr); @@ -879,7 +879,7 @@ ReturnedValue Document::load(ExecutionEngine *v4, const QByteArray &data) ScopedObject instance(scope, v4->memoryManager->allocate<Node>(document)); document->release(); // the GC should own the NodeImpl via Node now ScopedObject p(scope); - instance->setPrototype((p = Document::prototype(v4))); + instance->setPrototypeUnchecked((p = Document::prototype(v4))); return instance.asReturnedValue(); } @@ -1650,7 +1650,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine); Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r)); ScopedObject proto(scope, ctor->d()->proto); - w->setPrototype(proto); + w->setPrototypeUnchecked(proto); return w.asReturnedValue(); } diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index b5e25fb6d4..f99c4def45 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -223,13 +223,13 @@ static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) QV4::Scope scope(v4); bool instanceOfObject = false; - QV4::ScopedObject p(scope, object->prototype()); + QV4::ScopedObject p(scope, object->getPrototypeOf()); while (p) { if (p->d() == v4->objectPrototype()->d()) { instanceOfObject = true; break; } - p = p->prototype(); + p = p->getPrototypeOf(); } if (!instanceOfObject) return; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 62ccf0d66c..f98f57a8ed 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -2563,7 +2563,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) QV4::Scope scope(v4); QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem)); QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); - o->setPrototype(p); + o->setPrototypeOf(p); ++cacheItem->scriptRef; return QQmlV4Handle(o); @@ -3395,7 +3395,7 @@ public: QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); - object->setPrototype(changeProto); + object->setPrototypeOf(changeProto); object->d()->change = change; if (hasProperty) diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 3016316e57..4130eff33a 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -439,7 +439,7 @@ public: QV4::Scope scope(v4); QV4::ScopedObject proto(scope, type->prototype.value()); QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); - o->setPrototype(proto); + o->setPrototypeOf(proto); ++scriptRef; return o.asReturnedValue(); } @@ -620,7 +620,7 @@ public: QV4::Scope scope(v4); QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); QV4::ScopedObject p(scope, data->listItemProto.value()); - o->setPrototype(p); + o->setPrototypeOf(p); ++scriptRef; return o.asReturnedValue(); } |