Merge remote-tracking branch 'origin/5.13' into HEAD

Conflicts:
	src/qml/compiler/qv4compileddata_p.h
	src/qml/jit/qv4baselinejit.cpp
	src/qml/jit/qv4jithelpers.cpp
	src/qml/jsruntime/qv4lookup.cpp
	src/qml/jsruntime/qv4runtime.cpp
	src/qml/jsruntime/qv4runtimeapi_p.h
	src/qml/jsruntime/qv4vme_moth.cpp
	src/qml/qml/qqmltypemodule_p.h

Change-Id: If28793e9e08418457a11fc2c5832f03cab2fcc76
This commit is contained in:
Ulf Hermann 2019-03-22 14:47:51 +01:00
commit a768780f36
101 changed files with 1775 additions and 1788 deletions

101
dist/changes-5.12.2 vendored Normal file
View File

@ -0,0 +1,101 @@
Qt 5.12.2 is a bug-fix release. It maintains both forward and backward
compatibility (source and binary) with Qt 5.12.0 through 5.12.1.
For more details, refer to the online documentation included in this
distribution. The documentation is also available online:
https://doc.qt.io/qt-5/index.html
The Qt version 5.12 series is binary compatible with the 5.11.x series.
Applications compiled for 5.11 will continue to run with 5.12.
Some of the changes listed in this file include issue tracking numbers
corresponding to tasks in the Qt Bug Tracker:
https://bugreports.qt.io/
Each of these identifiers can be entered in the bug tracker to obtain more
information about a particular change.
****************************************************************************
* QtQml *
****************************************************************************
- Important Behavior Changes:
* The parameters passed to C++ functions from QML are now checked for
compatibility with the expected arguments. If they cannot be
converted, a warning is printed. In future versions of Qt a type error
will be thrown in JavaScript and the function will not be invoked.
* [QTBUG-73239] Removed revisions from the new Qt.labs.settings methods
and properties that were introduced in 5.12. Qt.labs plugins are
intended to always have revision 1.0 until they graduate.
- QQmlApplicationEngine:
* [QTBUG-73649] QQmlApplicationEngine connects quit() and exit() signals
with queued connections to avoid problems with AutoConnection, when
connecting to QCoreApplication slots.
- [QTBUG-69340] QML cache files are not unnecessarily re-generated.
- [QTBUG-71325] Fixed a crash in V4 string to number conversion on 32-bit
platforms.
- [QTBUG-72137] Fixed a crash in QML garbage collector when accessing
other items from Component.onDestruction.
- [QTBUG-72352] QML can be built with -no-feature-translation.
- [QTBUG-72407] We now annotate stack traces when frames are elided
through tail calls.
- [QTBUG-72734] Fixed a crash in the parser on certain kinds of bad input.
- [QTBUG-72858] Exception handlers are correctly scoped for try blocks and
for "for ... in" loops.
- [QTBUG-72908] QML can be built with -c++std=c++11 again.
- [QTBUG-72972] QQmlMetaType deletes attached properties in its destructor
to avoid a crash.
- [QTBUG-73009] Fixed a crash with qt.qml.binding.removal.info=true.
- [QTBUG-73013] If a signal sender is deleted during the handling of the
signal in QML, the QML engine won't crash anymore.
- [QTBUG-73152] Brought behavior of String.replace() in line with other
JS engines: "x".replace("x", "$1") gives "$1" in both JSC and V8, as there
are no captures that could be used as a replacement for $1. Two-digit
captures ($nm) get applied if $nm captures exist. If there are less than nm
but more than n captures available $n is replaced by the n'th capture and m
is copied over verbatim.
- [QTBUG-73425] Fixed allocation of large arrays at startup.
- [QTBUG-73733] Fixed an access-after-delete crash in DelegateModel.
- [QTBUG-73734] When a Q_GADGET type, marked as a primitive type with
Q_DECLARE_METATYPE, is emitted with a signal, it can now be accessed in QML.
- [QTBUG-73750] Fixed undefined Q_ENUM value in QML Connections object.
- [QTBUG-73821] Fixed a failing assert on 32bit platforms.
- The JIT compiler is disabled for the IPL32 (or X32) ABI. It did not work
before.
- The tail call optimization correctly counts method arguments on 32bit
platforms now.
- The JavaScript engine now tolerates UINT_MAX as array index.
****************************************************************************
* QtQuick *
****************************************************************************
- TextInput/security:
* When the TextInput is used for password input, preallocate a buffer
for the string that stores the entered value and zero-out the string
on TextInput destruction to avoid leaking sensitive data to process
memory
- [QTBUG-63271] If a MouseArea sets itself invisible or disabled while
handling a mouse press, it does not acquire the exclusive grab
- [QTBUG-71887] TapHandler now consistently forgets touchpoints that occur
outside its parent's bounds. This eliminates the warning "pointId is
missing from current event, but was neither canceled nor released" when
tapping quickly and having some of the taps fall out of bounds.
(The warning still exists though, in case there are other scenarios where
Handlers remember "wanting" certain touchpoints and then they go missing.)
- [QTBUG-72822] PinchHandler now correctly holds its target in place when
axes are disabled.
- [QTBUG-71918] PointerHandlers are declared as direct children of
Flickable (ListView, TableView etc.) now get the pointer events properly.
- [QTBUG-42155] Canvas now handles switching between object and string
based colors
- [QTBUG-73113] Fixed a crash when reparenting QML Canvas items
- [QTBUG-73013] Fixed a crash in QuickView on setSource while deleting
the sender.
- [QT3DS-1419] Improved quality of Qt 3D Studio text labels.

View File

@ -97,6 +97,8 @@
\sa StackView
*/
QT_BEGIN_NAMESPACE
QQuickStackLayout::QQuickStackLayout(QQuickItem *parent) :
QQuickLayout(*new QQuickStackLayoutPrivate, parent)
{
@ -345,4 +347,6 @@ bool QQuickStackLayout::shouldIgnoreItem(QQuickItem *item) const
return ignored;
}
QT_END_NAMESPACE
#include "moc_qquickstacklayout_p.cpp"

View File

@ -42,6 +42,8 @@
#include <qquicklayout_p.h>
QT_BEGIN_NAMESPACE
class QQuickStackLayoutPrivate;
class QQuickStackLayout : public QQuickLayout
@ -105,4 +107,6 @@ private:
bool explicitCurrentIndex;
};
QT_END_NAMESPACE
#endif // QQUICKSTACKLAYOUT_H

View File

@ -566,7 +566,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiQualifiedId *id)
void IRBuilder::accept(QQmlJS::AST::Node *node)
{
QQmlJS::AST::Node::acceptChild(node, this);
QQmlJS::AST::Node::accept(node, this);
}
bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride)
@ -974,7 +974,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
foe->node = funDecl;
foe->parentNode = funDecl;
foe->nameIndex = registerString(funDecl->name.toString());
foe->disableAcceleratedLookups = false;
const int index = _object->functionsAndExpressions->append(foe);
Function *f = New<Function>();
@ -1098,7 +1097,6 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
expr->parentNode = parentNode;
expr->nameIndex = registerString(QLatin1String("expression for ")
+ stringAt(binding->propertyNameIndex));
expr->disableAcceleratedLookups = false;
const int index = bindingsTarget()->functionsAndExpressions->append(expr);
binding->value.compiledScriptIndex = index;
// We don't need to store the binding script as string, except for script strings
@ -1825,19 +1823,13 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding
JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine,
QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports,
QQmlJS::AST::UiProgram *qmlRoot,
const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames)
: QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false)
, sourceCode(sourceCode)
, jsEngine(jsEngine)
, qmlRoot(qmlRoot)
, imports(imports)
, stringPool(stringPool)
, _disableAcceleratedLookups(false)
, _contextObject(nullptr)
, _scopeObject(nullptr)
, _qmlContextSlot(-1)
, _importedScriptsSlot(-1)
{
m_globalNames = globalNames;
@ -1845,18 +1837,6 @@ JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *
_fileNameIsUrl = true;
}
void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject)
{
_idObjects = objectIds;
_contextObject = contextObject;
_scopeObject = nullptr;
}
void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
{
_scopeObject = scopeObject;
}
QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
{
auto qmlName = [&](const CompiledFunctionOrExpression &c) {
@ -1921,7 +1901,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
body = body->finish();
}
_disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
function ? function->formals : nullptr,
body);
@ -1931,391 +1910,6 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body)
{
int qmlContextTemp = -1;
int importedScriptsTemp = -1;
qSwap(_qmlContextSlot, qmlContextTemp);
qSwap(_importedScriptsSlot, importedScriptsTemp);
int result = Codegen::defineFunction(name, ast, formals, body);
qSwap(_importedScriptsSlot, importedScriptsTemp);
qSwap(_qmlContextSlot, qmlContextTemp);
return result;
}
#ifndef V4_BOOTSTRAP
QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name)
{
QQmlPropertyData *pd = cache->property(name, /*object*/nullptr, /*context*/nullptr);
if (pd && !cache->isAllowedInRevision(pd))
return nullptr;
return pd;
}
enum MetaObjectResolverFlags {
AllPropertiesAreFinal = 0x1,
LookupsIncludeEnums = 0x2,
LookupsExcludeProperties = 0x4,
ResolveTypeInformationOnly = 0x8
};
#if 0
static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index);
static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine,
const QV4::IR::MemberExpressionResolver *resolver,
QV4::IR::Member *member)
{
QV4::IR::Type result = QV4::IR::VarType;
QQmlType type = resolver->qmlType;
if (member->name->constData()->isUpper()) {
bool ok = false;
int value = type.enumValue(qmlEngine, *member->name, &ok);
if (ok) {
member->setEnumValue(value);
return QV4::IR::SInt32Type;
} else {
int index = type.scopedEnumIndex(qmlEngine, *member->name, &ok);
if (ok) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initScopedEnumResolver(newResolver, type, index);
return QV4::IR::DiscoveredType(newResolver);
}
}
}
if (type.isCompositeSingleton()) {
QQmlRefPointer<QQmlTypeData> tdata = qmlEngine->typeLoader.getType(type.singletonInstanceInfo()->url);
Q_ASSERT(tdata);
tdata->release(); // Decrease the reference count added from QQmlTypeLoader::getType()
// When a singleton tries to reference itself, it may not be complete yet.
if (tdata->isComplete()) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId));
newResolver->flags |= AllPropertiesAreFinal;
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
} else if (type.isSingleton()) {
const QMetaObject *singletonMeta = type.singletonInstanceInfo()->instanceMetaObject;
if (singletonMeta) { // QJSValue-based singletons cannot be accelerated
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initMetaObjectResolver(newResolver, qmlEngine->cache(singletonMeta));
member->kind = QV4::IR::Member::MemberOfSingletonObject;
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
}
#if 0
else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) {
// Right now the attached property IDs are not stable and cannot be embedded in the
// code that is cached on disk.
QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta);
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initMetaObjectResolver(newResolver, cache);
member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine));
return newResolver->resolveMember(qmlEngine, newResolver, member);
}
#endif
return result;
}
static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType)
{
Q_ASSERT(resolver);
resolver->resolveMember = &resolveQmlType;
resolver->qmlType = qmlType;
resolver->typenameCache = 0;
resolver->flags = 0;
}
static QV4::IR::DiscoveredType resolveImportNamespace(
QQmlEnginePrivate *, const QV4::IR::MemberExpressionResolver *resolver,
QV4::IR::Member *member)
{
QV4::IR::Type result = QV4::IR::VarType;
QQmlTypeNameCache *typeNamespace = resolver->typenameCache;
const QQmlImportRef *importNamespace = resolver->import;
QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace);
if (r.isValid()) {
member->freeOfSideEffects = true;
if (r.scriptIndex != -1) {
// TODO: remember the index and replace with subscript later.
result = QV4::IR::VarType;
} else if (r.type.isValid()) {
// TODO: Propagate singleton information, so that it is loaded
// through the singleton getter in the run-time. Until then we
// can't accelerate access :(
if (!r.type.isSingleton()) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initQmlTypeResolver(newResolver, r.type);
return QV4::IR::DiscoveredType(newResolver);
}
} else {
Q_ASSERT(false); // How can this happen?
}
}
return result;
}
static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver,
QQmlTypeNameCache *imports, const QQmlImportRef *importNamespace)
{
resolver->resolveMember = &resolveImportNamespace;
resolver->import = importNamespace;
resolver->typenameCache = imports;
resolver->flags = 0;
}
static QV4::IR::DiscoveredType resolveMetaObjectProperty(
QQmlEnginePrivate *qmlEngine, const QV4::IR::MemberExpressionResolver *resolver,
QV4::IR::Member *member)
{
QV4::IR::Type result = QV4::IR::VarType;
QQmlPropertyCache *metaObject = resolver->propertyCache;
if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) {
const QMetaObject *mo = metaObject->createMetaObject();
QByteArray enumName = member->name->toUtf8();
for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) {
QMetaEnum metaEnum = mo->enumerator(ii);
bool ok;
int value = metaEnum.keyToValue(enumName.constData(), &ok);
if (ok) {
member->setEnumValue(value);
return QV4::IR::SInt32Type;
}
}
}
if (member->kind != QV4::IR::Member::MemberOfIdObjectsArray && member->kind != QV4::IR::Member::MemberOfSingletonObject &&
qmlEngine && !(resolver->flags & LookupsExcludeProperties)) {
QQmlPropertyData *property = member->property;
if (!property && metaObject) {
if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) {
const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal))
&& !candidate->isFunction();
if (lookupHints()
&& !(resolver->flags & AllPropertiesAreFinal)
&& !candidate->isFinal()
&& !candidate->isFunction()
&& candidate->isDirect()) {
qWarning() << "Hint: Access to property" << *member->name << "of" << metaObject->className() << "could be accelerated if it was marked as FINAL";
}
if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) {
property = candidate;
member->inhibitTypeConversionOnWrite = true;
if (!(resolver->flags & ResolveTypeInformationOnly))
member->property = candidate; // Cache for next iteration and isel needs it.
}
}
}
if (property) {
// Enums cannot be mapped to IR types, they need to go through the run-time handling
// of accepting strings that will then be converted to the right values.
if (property->isEnum())
return QV4::IR::VarType;
switch (property->propType()) {
case QMetaType::Bool: result = QV4::IR::BoolType; break;
case QMetaType::Int: result = QV4::IR::SInt32Type; break;
case QMetaType::Double: result = QV4::IR::DoubleType; break;
case QMetaType::QString: result = QV4::IR::StringType; break;
default:
if (property->isQObject()) {
if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initMetaObjectResolver(newResolver, cache);
return QV4::IR::DiscoveredType(newResolver);
}
} else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) {
if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) {
auto newResolver = resolver->owner->New<QV4::IR::MemberExpressionResolver>();
newResolver->owner = resolver->owner;
initMetaObjectResolver(newResolver, cache);
newResolver->flags |= ResolveTypeInformationOnly;
return QV4::IR::DiscoveredType(newResolver);
}
}
break;
}
}
}
return result;
}
static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject)
{
Q_ASSERT(resolver);
resolver->resolveMember = &resolveMetaObjectProperty;
resolver->propertyCache = metaObject;
resolver->flags = 0;
}
static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine,
const QV4::IR::MemberExpressionResolver *resolver,
QV4::IR::Member *member)
{
if (!member->name->constData()->isUpper())
return QV4::IR::VarType;
QQmlType type = resolver->qmlType;
int index = resolver->flags;
bool ok = false;
int value = type.scopedEnumValue(qmlEngine, index, *member->name, &ok);
if (!ok)
return QV4::IR::VarType;
member->setEnumValue(value);
return QV4::IR::SInt32Type;
}
static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index)
{
Q_ASSERT(resolver);
resolver->resolveMember = &resolveScopedEnum;
resolver->qmlType = qmlType;
resolver->flags = index;
}
#endif
#endif // V4_BOOTSTRAP
void JSCodeGen::beginFunctionBodyHook()
{
_qmlContextSlot = bytecodeGenerator->newRegister();
_importedScriptsSlot = bytecodeGenerator->newRegister();
#ifndef V4_BOOTSTRAP
Instruction::LoadQmlContext load;
load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
bytecodeGenerator->addInstruction(load);
#if 0
temp->type = QV4::IR::QObjectType;
temp->memberResolver = _function->New<QV4::IR::MemberExpressionResolver>();
initMetaObjectResolver(temp->memberResolver, _scopeObject);
auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0);
name->type = temp->type;
#endif
Instruction::LoadQmlImportedScripts loadScripts;
loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot();
bytecodeGenerator->addInstruction(loadScripts);
#endif
}
QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name)
{
#ifndef V4_BOOTSTRAP
if (_disableAcceleratedLookups)
return Reference();
// Implement QML lookup semantics in the current file context.
//
// Note: We do not check if properties of the qml scope object or context object
// are final. That's because QML tries to get as close as possible to lexical scoping,
// which means in terms of properties that only those visible at compile time are chosen.
// I.e. access to a "foo" property declared within the same QML component as "property int foo"
// will always access that instance and as integer. If a sub-type implements its own property string foo,
// then that one is not chosen for accesses from within this file, because it wasn't visible at compile
// time. This corresponds to the logic in QQmlPropertyCache::findProperty to find the property associated
// with the correct QML context.
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
if (_context->contextType == QV4::Compiler::ContextType::Binding)
_context->idObjectDependencies.insert(mapping.idIndex);
Instruction::LoadIdObject load;
load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot();
load.index = mapping.idIndex;
Reference result = Reference::fromAccumulator(this);
bytecodeGenerator->addInstruction(load);
result.isReadonly = true;
return result;
}
}
if (name.at(0).isUpper()) {
QQmlTypeNameCache::Result r = imports->query(name);
if (r.isValid()) {
if (r.scriptIndex != -1) {
Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot);
return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex)));
} else if (r.type.isValid()) {
return Reference::fromName(this, name);
} else {
Q_ASSERT(r.importNamespace);
return Reference::fromName(this, name);
}
}
}
if (_scopeObject) {
QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name);
if (data) {
// Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
if (data->isFunction())
return Reference::fromName(this, name);
Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
Reference::PropertyCapturePolicy capturePolicy;
if (!data->isConstant() && !data->isQmlBinding())
capturePolicy = Reference::CaptureAtRuntime;
else
capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
}
}
if (_contextObject) {
QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name);
if (data) {
// Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time
if (data->isFunction())
return Reference::fromName(this, name);
Reference base = Reference::fromStackSlot(this, _qmlContextSlot);
Reference::PropertyCapturePolicy capturePolicy;
if (!data->isConstant() && !data->isQmlBinding())
capturePolicy = Reference::CaptureAtRuntime;
else
capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime;
return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy);
}
}
#else
Q_UNUSED(name)
#endif // V4_BOOTSTRAP
return Reference();
}
#ifndef V4_BOOTSTRAP
QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const
@ -2435,7 +2029,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
b->value.compiledScriptIndex = functionIndices.count() - 1;
QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>();
foe->disableAcceleratedLookups = true;
foe->nameIndex = 0;
QQmlJS::AST::ExpressionNode *expr;

View File

@ -278,7 +278,6 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression
QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression
quint32 nameIndex = 0;
bool disableAcceleratedLookups = false;
CompiledFunctionOrExpression *next = nullptr;
};
@ -431,6 +430,12 @@ public:
bool visit(QQmlJS::AST::UiScriptBinding *ast) override;
bool visit(QQmlJS::AST::UiSourceElement *ast) override;
void throwRecursionDepthError() override
{
recordError(AST::SourceLocation(),
QStringLiteral("Maximum statement or expression depth exceeded"));
}
void accept(QQmlJS::AST::Node *node);
// returns index in _objects
@ -533,47 +538,16 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
{
JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule,
QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot,
QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
struct IdMapping
{
QString name;
int idIndex;
QQmlPropertyCache *type;
};
typedef QVector<IdMapping> ObjectIdMapping;
void beginContextScope(const ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject);
void beginObjectScope(QQmlPropertyCache *scopeObject);
const QV4::Compiler::StringTableGenerator *stringPool, const QSet<QString> &globalNames);
// Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions
QVector<int> generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions);
int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
AST::StatementList *body) override;
protected:
void beginFunctionBodyHook() override;
bool canAccelerateGlobalLookups() const override { return !_disableAcceleratedLookups; }
Reference fallbackNameLookup(const QString &name) override;
private:
// returns nullptr if lookup needs to happen by name
QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name);
QString sourceCode;
QQmlJS::Engine *jsEngine; // needed for memory pool
QQmlJS::AST::UiProgram *qmlRoot;
QQmlTypeNameCache *imports;
const QV4::Compiler::StringTableGenerator *stringPool;
bool _disableAcceleratedLookups;
ObjectIdMapping _idObjects;
QQmlPropertyCache *_contextObject;
QQmlPropertyCache *_scopeObject;
int _qmlContextSlot;
int _importedScriptsSlot;
};
struct Q_QML_PRIVATE_EXPORT IRLoader {

View File

@ -146,8 +146,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
document->jsModule.fileName = typeData->urlString();
document->jsModule.finalUrl = typeData->finalUrlString();
QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine,
document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
v4CodeGenerator.setUseFastLookups(false);
document->program, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
return nullptr;
@ -767,10 +766,6 @@ void QQmlScriptStringScanner::scan()
if (!pd || pd->propType() != scriptStringMetaType)
continue;
QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
if (foe)
foe->disableAcceleratedLookups = true;
QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex);
binding->stringIndex = compiler->registerString(script);
}
@ -1324,24 +1319,6 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject)
contextObject = componentBinding->value.objectIndex;
}
QmlIR::JSCodeGen::ObjectIdMapping idMapping;
idMapping.reserve(obj->namedObjectsInComponent.size());
for (int i = 0; i < obj->namedObjectsInComponent.size(); ++i) {
const int objectIndex = obj->namedObjectsInComponent.at(i);
QmlIR::JSCodeGen::IdMapping m;
const QmlIR::Object *obj = qmlObjects.at(objectIndex);
m.name = stringAt(obj->idNameIndex);
m.idIndex = obj->id;
m.type = propertyCaches->at(objectIndex);
auto *tref = resolvedType(obj->inheritedTypeNameIndex);
if (tref && tref->isFullyDynamicType)
m.type = nullptr;
idMapping << m;
}
v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject));
if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject))
return false;
@ -1355,16 +1332,9 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn
return true;
if (object->functionsAndExpressions->count > 0) {
QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex);
v4CodeGen->beginObjectScope(scopeObject);
QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) {
const bool haveCustomParser = customParsers.contains(object->inheritedTypeNameIndex);
if (haveCustomParser)
foe->disableAcceleratedLookups = true;
for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
functionsToCompile << *foe;
}
const QVector<int> runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile);
const QList<QQmlError> jsErrors = v4CodeGen->qmlErrors();
if (!jsErrors.isEmpty()) {

View File

@ -100,9 +100,10 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
, hasError(false)
{
jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
pushExpr();
}
const char *globalNames[] = {
const char *Codegen::s_globalNames[] = {
"isNaN",
"parseFloat",
"String",
@ -182,7 +183,7 @@ void Codegen::generateFromProgram(const QString &fileName,
//
// Since this can be called from the loader thread we can't get the list
// directly from the engine, so let's hardcode the most important ones here
for (const char **g = globalNames; *g != nullptr; ++g)
for (const char **g = s_globalNames; *g != nullptr; ++g)
m_globalNames << QString::fromLatin1(*g);
}
@ -264,7 +265,7 @@ Context *Codegen::enterBlock(Node *node)
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
return _expr.result();
return exprResult();
if (expr.isConstant()) {
auto v = Value::fromReturnedValue(expr.constant);
@ -310,7 +311,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
return Reference::fromAccumulator(this);
}
case PostIncrement:
if (!_expr.accept(nx) || requiresReturnValue) {
if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
Instruction::UPlus uplus = {};
@ -330,13 +331,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Increment inc = {};
bytecodeGenerator->addTracingInstruction(inc);
if (_expr.accept(nx))
if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
}
case PostDecrement:
if (!_expr.accept(nx) || requiresReturnValue) {
if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
Instruction::UPlus uplus = {};
@ -356,7 +357,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Decrement dec = {};
bytecodeGenerator->addTracingInstruction(dec);
if (_expr.accept(nx))
if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
@ -368,22 +369,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
void Codegen::addCJump()
{
bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(),
_expr.iftrue(), _expr.iffalse());
}
void Codegen::accept(Node *node)
{
if (hasError)
return;
if (node)
node->accept(this);
const Result &expression = currentExpr();
bytecodeGenerator->addCJumpInstruction(expression.trueBlockFollowsCondition(),
expression.iftrue(), expression.iffalse());
}
void Codegen::statement(Statement *ast)
{
RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
bytecodeGenerator->setLocation(ast->firstSourceLocation());
@ -399,23 +391,21 @@ void Codegen::statement(ExpressionNode *ast)
if (! ast) {
return;
} else {
RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
Result r(nx);
qSwap(_expr, r);
pushExpr(Result(nx));
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
qSwap(_volatileMemoryLocations, vLocs);
qSwap(_expr, r);
Reference result = popResult();
if (hasError)
return;
if (r.result().loadTriggersSideEffect())
r.result().loadInAccumulator(); // triggers side effects
if (result.loadTriggersSideEffect())
result.loadInAccumulator(); // triggers side effects
}
}
@ -428,11 +418,9 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
if (!ast)
return;
RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
Result r(iftrue, iffalse, trueBlockFollowsCondition);
qSwap(_expr, r);
pushExpr(Result(iftrue, iffalse, trueBlockFollowsCondition));
accept(ast);
qSwap(_expr, r);
Result r = popExpr();
if (hasError)
return;
@ -450,18 +438,6 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
}
}
Codegen::Reference Codegen::expression(ExpressionNode *ast)
{
RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
Result r;
if (ast) {
qSwap(_expr, r);
accept(ast);
qSwap(_expr, r);
}
return r.result();
}
void Codegen::program(Program *ast)
{
if (ast) {
@ -875,17 +851,13 @@ bool Codegen::visit(ExportDeclaration *ast)
Reference exportedValue;
if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
Result r;
qSwap(_expr, r);
pushExpr();
visit(static_cast<FunctionExpression*>(fdecl));
qSwap(_expr, r);
exportedValue = r.result();
exportedValue = popResult();
} else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
Result r;
qSwap(_expr, r);
pushExpr();
visit(static_cast<ClassExpression*>(classDecl));
qSwap(_expr, r);
exportedValue = r.result();
exportedValue = popResult();
} else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) {
exportedValue = expression(expr);
}
@ -1068,7 +1040,7 @@ bool Codegen::visit(ClassExpression *ast)
(void) ctor.storeRetainAccumulator();
}
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
@ -1151,7 +1123,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
if (!it) {
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
@ -1246,7 +1218,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
array.loadInAccumulator();
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
@ -1262,7 +1234,7 @@ bool Codegen::visit(ArrayMemberExpression *ast)
return false;
if (base.isSuper()) {
Reference index = expression(ast->expression).storeOnStack();
_expr.setResult(Reference::fromSuperProperty(index));
setExprResult(Reference::fromSuperProperty(index));
return false;
}
base = base.storeOnStack();
@ -1272,17 +1244,17 @@ bool Codegen::visit(ArrayMemberExpression *ast)
QString s = str->value.toString();
uint arrayIndex = QV4::String::toArrayIndex(s);
if (arrayIndex == UINT_MAX) {
_expr.setResult(Reference::fromMember(base, str->value.toString()));
setExprResult(Reference::fromMember(base, str->value.toString()));
return false;
}
Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
_expr.setResult(Reference::fromSubscript(base, index));
setExprResult(Reference::fromSubscript(base, index));
return false;
}
Reference index = expression(ast->expression);
if (hasError)
return false;
_expr.setResult(Reference::fromSubscript(base, index));
setExprResult(Reference::fromSubscript(base, index));
return false;
}
@ -1313,12 +1285,13 @@ bool Codegen::visit(BinaryExpression *ast)
TailCallBlocker blockTailCalls(this);
if (ast->op == QSOperator::And) {
if (_expr.accept(cx)) {
if (exprAccept(cx)) {
auto iftrue = bytecodeGenerator->newLabel();
condition(ast->left, &iftrue, _expr.iffalse(), true);
condition(ast->left, &iftrue, currentExpr().iffalse(), true);
iftrue.link();
blockTailCalls.unblock();
condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
const Result &expr = currentExpr();
condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iftrue = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@ -1340,15 +1313,16 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Or) {
if (_expr.accept(cx)) {
if (exprAccept(cx)) {
auto iffalse = bytecodeGenerator->newLabel();
condition(ast->left, _expr.iftrue(), &iffalse, false);
condition(ast->left, currentExpr().iftrue(), &iffalse, false);
iffalse.link();
condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
const Result &expr = currentExpr();
condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iffalse = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@ -1370,7 +1344,7 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Assign) {
@ -1381,9 +1355,9 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
right = right.storeOnStack();
destructurePattern(p, right);
if (!_expr.accept(nx)) {
if (!exprAccept(nx)) {
right.loadInAccumulator();
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
}
return false;
}
@ -1403,10 +1377,10 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
r.loadInAccumulator();
if (_expr.accept(nx))
_expr.setResult(left.storeConsumeAccumulator());
if (exprAccept(nx))
setExprResult(left.storeConsumeAccumulator());
else
_expr.setResult(left.storeRetainAccumulator());
setExprResult(left.storeRetainAccumulator());
return false;
}
@ -1449,7 +1423,7 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
_expr.setResult(left.storeRetainAccumulator());
setExprResult(left.storeRetainAccumulator());
break;
}
@ -1461,7 +1435,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right = expression(ast->right);
if (hasError)
return false;
_expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
break;
}
// intentional fall-through!
@ -1487,7 +1461,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right;
if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) {
visit(rhs);
right = _expr.result();
right = exprResult();
} else {
left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
right = expression(ast->right);
@ -1495,7 +1469,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
_expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
break;
}
@ -1672,7 +1646,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictEqual: {
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictEqual cmp;
@ -1683,7 +1657,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictNotEqual: {
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictNotEqual cmp;
@ -1694,7 +1668,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Equal: {
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpEq cmp;
@ -1705,7 +1679,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::NotEqual: {
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpNe cmp;
@ -1716,7 +1690,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Gt: {
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGt cmp;
@ -1727,7 +1701,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Ge: {
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGe cmp;
@ -1738,7 +1712,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Lt: {
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLt cmp;
@ -1749,7 +1723,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Le:
if (_expr.accept(cx))
if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLe cmp;
@ -1902,8 +1876,6 @@ bool Codegen::visit(CallExpression *ast)
switch (base.type) {
case Reference::Member:
case Reference::Subscript:
case Reference::QmlScopeObject:
case Reference::QmlContextObject:
base = base.asLValue();
break;
case Reference::Name:
@ -1953,7 +1925,7 @@ bool Codegen::visit(CallExpression *ast)
bytecodeGenerator->addInstruction(call);
}
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
@ -1965,21 +1937,7 @@ bool Codegen::visit(CallExpression *ast)
void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject)
{
//### Do we really need all these call instructions? can's we load the callee in a temp?
if (base.type == Reference::QmlScopeObject) {
Instruction::CallScopeObjectProperty call;
call.base = base.qmlBase.stackSlot();
call.name = base.qmlCoreIndex;
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
} else if (base.type == Reference::QmlContextObject) {
Instruction::CallContextObjectProperty call;
call.base = base.qmlBase.stackSlot();
call.name = base.qmlCoreIndex;
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
} else if (base.type == Reference::Member) {
if (base.type == Reference::Member) {
if (!disable_lookups && useFastLookups) {
Instruction::CallPropertyLookup call;
call.base = base.propertyBase.stackSlot();
@ -2009,11 +1967,19 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
} else if (!disable_lookups && useFastLookups && base.global) {
Instruction::CallGlobalLookup call;
call.index = registerGlobalGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
if (base.qmlGlobal) {
Instruction::CallQmlContextPropertyLookup call;
call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
} else {
Instruction::CallGlobalLookup call;
call.index = registerGlobalGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addTracingInstruction(call);
}
} else {
Instruction::CallName call;
call.name = base.nameAsIndex();
@ -2046,7 +2012,7 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
bytecodeGenerator->addTracingInstruction(call);
}
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
@ -2142,7 +2108,7 @@ bool Codegen::visit(ConditionalExpression *ast)
ko.loadInAccumulator();
jump_endif.link();
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
@ -2172,7 +2138,7 @@ bool Codegen::visit(DeleteExpression *ast)
throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
return false;
}
_expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
case Reference::Name: {
if (_context->isStrict) {
@ -2182,7 +2148,7 @@ bool Codegen::visit(DeleteExpression *ast)
Instruction::DeleteName del;
del.name = expr.nameAsIndex();
bytecodeGenerator->addInstruction(del);
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Member: {
@ -2197,7 +2163,7 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.propertyBase.stackSlot();
del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Subscript: {
@ -2207,14 +2173,14 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
default:
break;
}
// [[11.4.1]] Return true if it's not a reference
_expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@ -2223,7 +2189,7 @@ bool Codegen::visit(FalseLiteral *)
if (hasError)
return false;
_expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
}
@ -2232,7 +2198,7 @@ bool Codegen::visit(SuperLiteral *)
if (hasError)
return false;
_expr.setResult(Reference::fromSuper(this));
setExprResult(Reference::fromSuper(this));
return false;
}
@ -2250,12 +2216,12 @@ bool Codegen::visit(FieldMemberExpression *ast)
if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
Reference r = referenceForName(QStringLiteral("new.target"), false);
r.isReadonly = true;
_expr.setResult(r);
setExprResult(r);
return false;
}
Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
_expr.setResult(r);
setExprResult(r);
return false;
}
}
@ -2268,10 +2234,10 @@ bool Codegen::visit(FieldMemberExpression *ast)
load.stringId = registerString(ast->name.toString());
bytecodeGenerator->addInstruction(load);
Reference property = Reference::fromAccumulator(this).storeOnStack();
_expr.setResult(Reference::fromSuperProperty(property));
setExprResult(Reference::fromSuperProperty(property));
return false;
}
_expr.setResult(Reference::fromMember(base, ast->name.toString()));
setExprResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
@ -2281,12 +2247,15 @@ bool Codegen::visit(TaggedTemplate *ast)
return false;
RegisterScope scope(this);
return handleTaggedTemplate(expression(ast->base), ast);
}
int functionObject = -1, thisObject = -1;
Reference base = expression(ast->base);
bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
{
if (hasError)
return false;
int functionObject = -1, thisObject = -1;
switch (base.type) {
case Reference::Member:
case Reference::Subscript:
@ -2347,7 +2316,7 @@ bool Codegen::visit(FunctionExpression *ast)
if (hasError)
return false;
loadClosure(function);
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
@ -2379,14 +2348,10 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co
return r;
}
// This hook allows implementing QML lookup semantics
Reference fallback = fallbackNameLookup(name);
if (fallback.type != Reference::Invalid)
return fallback;
Reference r = Reference::fromName(this, name);
r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global);
if (!r.global && canAccelerateGlobalLookups() && m_globalNames.contains(name))
r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal);
r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal;
if (!r.global && !r.qmlGlobal && m_globalNames.contains(name))
r.global = true;
return r;
}
@ -2402,18 +2367,12 @@ void Codegen::loadClosure(int closureId)
}
}
Codegen::Reference Codegen::fallbackNameLookup(const QString &name)
{
Q_UNUSED(name)
return Reference();
}
bool Codegen::visit(IdentifierExpression *ast)
{
if (hasError)
return false;
_expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
return false;
}
@ -2463,7 +2422,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
// set the result up as the thisObject
Reference::fromAccumulator(this).storeOnStack(CallData::This);
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
}
bool Codegen::visit(NewExpression *ast)
@ -2512,7 +2471,7 @@ bool Codegen::visit(NotExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(Not, expression(ast->expression)));
setExprResult(unop(Not, expression(ast->expression)));
return false;
}
@ -2521,10 +2480,10 @@ bool Codegen::visit(NullExpression *)
if (hasError)
return false;
if (_expr.accept(cx))
bytecodeGenerator->jump().link(*_expr.iffalse());
if (exprAccept(cx))
bytecodeGenerator->jump().link(*currentExpr().iffalse());
else
_expr.setResult(Reference::fromConst(this, Encode::null()));
setExprResult(Reference::fromConst(this, Encode::null()));
return false;
}
@ -2534,7 +2493,7 @@ bool Codegen::visit(NumericLiteral *ast)
if (hasError)
return false;
_expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
return false;
}
@ -2646,8 +2605,7 @@ bool Codegen::visit(ObjectPattern *ast)
call.argc = argc;
call.args = Moth::StackSlot::createRegister(args);
bytecodeGenerator->addInstruction(call);
Reference result = Reference::fromAccumulator(this);
_expr.setResult(result);
setExprResult(Reference::fromAccumulator(this));
return false;
}
@ -2666,7 +2624,7 @@ bool Codegen::visit(PostDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
_expr.setResult(unop(PostDecrement, expr));
setExprResult(unop(PostDecrement, expr));
return false;
}
@ -2686,7 +2644,7 @@ bool Codegen::visit(PostIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
_expr.setResult(unop(PostIncrement, expr));
setExprResult(unop(PostIncrement, expr));
return false;
}
@ -2704,7 +2662,7 @@ bool Codegen::visit(PreDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
_expr.setResult(unop(PreDecrement, expr));
setExprResult(unop(PreDecrement, expr));
return false;
}
@ -2723,7 +2681,7 @@ bool Codegen::visit(PreIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
_expr.setResult(unop(PreIncrement, expr));
setExprResult(unop(PreIncrement, expr));
return false;
}
@ -2734,7 +2692,7 @@ bool Codegen::visit(RegExpLiteral *ast)
auto r = Reference::fromStackSlot(this);
r.isReadonly = true;
_expr.setResult(r);
setExprResult(r);
Instruction::MoveRegExp instr;
instr.regExpId = jsUnitGenerator->registerRegExp(ast);
@ -2750,7 +2708,7 @@ bool Codegen::visit(StringLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
_expr.setResult(r);
setExprResult(r);
Instruction::LoadRuntimeString instr;
instr.stringId = registerString(ast->value.toString());
@ -2800,7 +2758,7 @@ bool Codegen::visit(TemplateLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
_expr.setResult(r);
setExprResult(r);
return false;
}
@ -2813,10 +2771,10 @@ bool Codegen::visit(ThisExpression *)
if (_context->isArrowFunction) {
Reference r = referenceForName(QStringLiteral("this"), false);
r.isReadonly = true;
_expr.setResult(r);
setExprResult(r);
return false;
}
_expr.setResult(Reference::fromThis(this));
setExprResult(Reference::fromThis(this));
return false;
}
@ -2826,7 +2784,7 @@ bool Codegen::visit(TildeExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(Compl, expression(ast->expression)));
setExprResult(unop(Compl, expression(ast->expression)));
return false;
}
@ -2835,7 +2793,7 @@ bool Codegen::visit(TrueLiteral *)
if (hasError)
return false;
_expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@ -2861,7 +2819,7 @@ bool Codegen::visit(TypeOfExpression *ast)
Instruction::TypeofValue instr;
bytecodeGenerator->addInstruction(instr);
}
_expr.setResult(Reference::fromAccumulator(this));
setExprResult(Reference::fromAccumulator(this));
return false;
}
@ -2872,7 +2830,7 @@ bool Codegen::visit(UnaryMinusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(UMinus, expression(ast->expression)));
setExprResult(unop(UMinus, expression(ast->expression)));
return false;
}
@ -2882,7 +2840,7 @@ bool Codegen::visit(UnaryPlusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(UPlus, expression(ast->expression)));
setExprResult(unop(UPlus, expression(ast->expression)));
return false;
}
@ -2895,7 +2853,7 @@ bool Codegen::visit(VoidExpression *ast)
TailCallBlocker blockTailCalls(this);
statement(ast->expression);
_expr.setResult(Reference::fromConst(this, Encode::undefined()));
setExprResult(Reference::fromConst(this, Encode::undefined()));
return false;
}
@ -2909,7 +2867,7 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (_functionContext->contextType == ContextType::Binding)
referenceForName(ast->name.toString(), true).loadInAccumulator();
_expr.accept(nx);
exprAccept(nx);
return false;
}
@ -2965,7 +2923,7 @@ bool Codegen::visit(YieldExpression *ast)
done.link();
lhsValue.loadInAccumulator();
_expr.setResult(acc);
setExprResult(acc);
return false;
}
@ -2976,7 +2934,7 @@ bool Codegen::visit(YieldExpression *ast)
BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
emitReturn(acc);
jump.link();
_expr.setResult(acc);
setExprResult(acc);
return false;
}
@ -3115,8 +3073,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
bytecodeGenerator->addInstruction(yield);
}
beginFunctionBodyHook();
statementList(body);
if (!hasError) {
@ -3855,8 +3811,14 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading()
class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor
{
VolatileMemoryLocations locs;
Codegen *parent;
public:
VolatileMemoryLocationScanner(Codegen *parent) :
QQmlJS::AST::Visitor(parent->recursionDepth()),
parent(parent)
{}
Codegen::VolatileMemoryLocations scan(AST::Node *s)
{
s->accept(this);
@ -3921,25 +3883,41 @@ public:
}
}
void throwRecursionDepthError() override
{
parent->throwRecursionDepthError();
}
private:
void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const {
void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) {
class Collector: public QQmlJS::AST::Visitor {
private:
QVector<QStringView> &ids;
VolatileMemoryLocationScanner *parent;
public:
Collector(QVector<QStringView> &ids): ids(ids) {}
virtual bool visit(IdentifierExpression *ie) {
Collector(QVector<QStringView> &ids, VolatileMemoryLocationScanner *parent) :
QQmlJS::AST::Visitor(parent->recursionDepth()), ids(ids), parent(parent)
{}
bool visit(IdentifierExpression *ie) final {
ids.append(ie->name);
return false;
}
void throwRecursionDepthError() final
{
parent->throwRecursionDepthError();
}
};
Collector collector(ids);
Collector collector(ids, this);
node->accept(&collector);
}
};
Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const
Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast)
{
VolatileMemoryLocationScanner scanner;
VolatileMemoryLocationScanner scanner(this);
return scanner.scan(ast);
}
@ -4041,10 +4019,6 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const
return index == other.index;
case Const:
return constant == other.constant;
case QmlScopeObject:
case QmlContextObject:
return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex
&& capturePolicy == other.capturePolicy;
}
return true;
}
@ -4102,9 +4076,7 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
Codegen::Reference Codegen::Reference::baseObject() const
{
if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) {
return Reference::fromStackSlot(codegen, qmlBase.stackSlot());
} else if (type == Reference::Member) {
if (type == Reference::Member) {
RValue rval = propertyBase;
if (!rval.isValid())
return Reference::fromConst(codegen, Encode::undefined());
@ -4189,8 +4161,6 @@ bool Codegen::Reference::storeWipesAccumulator() const
case Name:
case Member:
case Subscript:
case QmlScopeObject:
case QmlContextObject:
return true;
}
}
@ -4270,18 +4240,6 @@ void Codegen::Reference::storeAccumulator() const
store.index = elementSubscript.stackSlot();
codegen->bytecodeGenerator->addTracingInstruction(store);
} return;
case QmlScopeObject: {
Instruction::StoreScopeObjectProperty store;
store.base = qmlBase;
store.propertyIndex = qmlCoreIndex;
codegen->bytecodeGenerator->addInstruction(store);
} return;
case QmlContextObject: {
Instruction::StoreContextObjectProperty store;
store.base = qmlBase;
store.propertyIndex = qmlCoreIndex;
codegen->bytecodeGenerator->addInstruction(store);
} return;
case Invalid:
case Accumulator:
case Const:
@ -4396,9 +4354,15 @@ QT_WARNING_POP
}
}
if (!disable_lookups && global) {
Instruction::LoadGlobalLookup load;
load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
codegen->bytecodeGenerator->addTracingInstruction(load);
if (qmlGlobal) {
Instruction::LoadQmlContextPropertyLookup load;
load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex());
codegen->bytecodeGenerator->addTracingInstruction(load);
} else {
Instruction::LoadGlobalLookup load;
load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
codegen->bytecodeGenerator->addTracingInstruction(load);
}
} else {
Instruction::LoadName load;
load.name = nameAsIndex();
@ -4432,24 +4396,6 @@ QT_WARNING_POP
load.base = elementBase;
codegen->bytecodeGenerator->addTracingInstruction(load);
} return;
case QmlScopeObject: {
Instruction::LoadScopeObjectProperty load;
load.base = qmlBase;
load.propertyIndex = qmlCoreIndex;
load.captureRequired = capturePolicy == CaptureAtRuntime;
codegen->bytecodeGenerator->addInstruction(load);
if (capturePolicy == CaptureAheadOfTime)
codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
} return;
case QmlContextObject: {
Instruction::LoadContextObjectProperty load;
load.base = qmlBase;
load.propertyIndex = qmlCoreIndex;
load.captureRequired = capturePolicy == CaptureAtRuntime;
codegen->bytecodeGenerator->addInstruction(load);
if (capturePolicy == CaptureAheadOfTime)
codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex);
} return;
case Invalid:
break;
}

View File

@ -184,16 +184,31 @@ public:
Member,
Subscript,
Import,
QmlScopeObject,
QmlContextObject,
LastLValue = QmlContextObject,
LastLValue = Import,
Const
} type = Invalid;
bool isLValue() const { return !isReadonly && type > Accumulator; }
Reference(Codegen *cg, Type type = Invalid) : type(type), constant(0), codegen(cg) {}
Reference(): constant(0) {}
Reference(Codegen *cg, Type t = Invalid) : Reference()
{
type = t;
codegen = cg;
}
Reference() :
constant(0),
isArgOrEval(false),
isReadonly(false),
isReferenceToConst(false),
requiresTDZCheck(false),
subscriptRequiresTDZCheck(false),
stackSlotIsLocalOrArgument(false),
isVolatile(false),
global(false),
qmlGlobal(false)
{}
Reference(const Reference &) = default;
Reference(Reference &&) = default;
Reference &operator =(const Reference &) = default;
@ -206,10 +221,6 @@ public:
bool isValid() const { return type != Invalid; }
bool loadTriggersSideEffect() const {
switch (type) {
case QmlScopeObject:
return capturePolicy != DontCapture;
case QmlContextObject:
return capturePolicy != DontCapture;
case Name:
case Member:
case Subscript:
@ -228,28 +239,6 @@ public:
return isStackSlot();
}
enum PropertyCapturePolicy {
/*
We're reading a property from the scope or context object, but it's a CONSTANT property,
so we don't need to register a dependency at all.
*/
DontCapture,
/*
We're reading the property of a QObject, and we know that it's the
scope object or context object, which we know very well. Instead of registering a
property capture every time, we can do that ahead of time and then register all those
captures in one shot in registerQmlDependencies().
*/
CaptureAheadOfTime,
/*
We're reading the property of a QObject, and we're not quite sure where
the QObject comes from or what it is. So, when reading that property at run-time,
make sure that we capture where we read that property so that if it changes we can
re-evaluate the entire expression.
*/
CaptureAtRuntime
};
static Reference fromAccumulator(Codegen *cg) {
return Reference(cg, Accumulator);
}
@ -316,22 +305,6 @@ public:
r.isReadonly = true;
return r;
}
static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
Reference r(base.codegen, QmlScopeObject);
r.qmlBase = base.storeOnStack().stackSlot();
r.qmlCoreIndex = coreIndex;
r.qmlNotifyIndex = notifyIndex;
r.capturePolicy = capturePolicy;
return r;
}
static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) {
Reference r(base.codegen, QmlContextObject);
r.qmlBase = base.storeOnStack().stackSlot();
r.qmlCoreIndex = coreIndex;
r.qmlNotifyIndex = notifyIndex;
r.capturePolicy = capturePolicy;
return r;
}
static Reference fromThis(Codegen *cg) {
Reference r = fromStackSlot(cg, CallData::This);
r.isReadonly = true;
@ -386,25 +359,21 @@ public:
Moth::StackSlot elementBase;
RValue elementSubscript;
};
struct { // QML scope/context object case
Moth::StackSlot qmlBase;
qint16 qmlCoreIndex;
qint16 qmlNotifyIndex;
PropertyCapturePolicy capturePolicy;
};
Moth::StackSlot property; // super property
};
QString name;
mutable bool isArgOrEval = false;
bool isReadonly = false;
bool isReferenceToConst = false;
bool requiresTDZCheck = false;
bool subscriptRequiresTDZCheck = false;
bool stackSlotIsLocalOrArgument = false;
bool isVolatile = false;
bool global = false;
Codegen *codegen = nullptr;
quint32 isArgOrEval:1;
quint32 isReadonly:1;
quint32 isReferenceToConst:1;
quint32 requiresTDZCheck:1;
quint32 subscriptRequiresTDZCheck:1;
quint32 stackSlotIsLocalOrArgument:1;
quint32 isVolatile:1;
quint32 global:1;
quint32 qmlGlobal:1;
private:
void storeAccumulator() const;
Reference doStoreOnStack(int tempIndex) const;
@ -499,6 +468,10 @@ protected:
void setResult(const Reference &result) {
_result = result;
}
void setResult(Reference &&result) {
_result = std::move(result);
}
};
void enterContext(AST::Node *node);
@ -532,6 +505,7 @@ public:
int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); }
int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); }
int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); }
int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); }
// Returns index in _module->functions
virtual int defineFunction(const QString &name, AST::Node *ast,
@ -544,9 +518,22 @@ protected:
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
Reference expression(AST::ExpressionNode *ast);
void accept(AST::Node *node);
inline Reference expression(AST::ExpressionNode *ast)
{
if (!ast || hasError)
return Reference();
pushExpr();
ast->accept(this);
return popResult();
}
inline void accept(AST::Node *node)
{
if (!hasError && node)
node->accept(this);
}
void program(AST::Program *ast);
void statementList(AST::StatementList *ast);
@ -561,12 +548,6 @@ protected:
Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
// Hooks provided to implement QML lookup semantics
virtual bool canAccelerateGlobalLookups() const { return true; }
virtual Reference fallbackNameLookup(const QString &name);
virtual void beginFunctionBodyHook() {}
void emitReturn(const Reference &expr);
// nodes
@ -670,6 +651,11 @@ protected:
bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc);
virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
void throwRecursionDepthError() override
{
throwSyntaxError(AST::SourceLocation(),
QStringLiteral("Maximum statement or expression depth exceeded"));
}
public:
QList<DiagnosticMessage> errors() const;
@ -684,6 +670,7 @@ public:
void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject);
Arguments pushTemplateArgs(AST::TemplateLiteral *args);
bool handleTaggedTemplate(Reference base, AST::TaggedTemplate *ast);
void createTemplateObject(AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
@ -714,13 +701,40 @@ public:
m_globalNames = globalNames;
}
static const char *s_globalNames[];
protected:
friend class ScanFunctions;
friend struct ControlFlow;
friend struct ControlFlowCatch;
friend struct ControlFlowFinally;
Result _expr;
inline void setExprResult(const Reference &result) { m_expressions.back().setResult(result); }
inline void setExprResult(Reference &&result) { m_expressions.back().setResult(std::move(result)); }
inline Reference exprResult() const { return m_expressions.back().result(); }
inline bool exprAccept(Format f) { return m_expressions.back().accept(f); }
inline const Result &currentExpr() const { return m_expressions.back(); }
inline void pushExpr(Result &&expr) { m_expressions.push_back(std::move(expr)); }
inline void pushExpr(const Result &expr) { m_expressions.push_back(expr); }
inline void pushExpr() { m_expressions.emplace_back(); }
inline Result popExpr()
{
const Result result = m_expressions.back();
m_expressions.pop_back();
return result;
}
inline Reference popResult() {
const Reference result = m_expressions.back().result();
m_expressions.pop_back();
return result;
}
std::vector<Result> m_expressions;
VolatileMemoryLocations _volatileMemoryLocations;
Module *_module;
int _returnAddress;
@ -769,33 +783,8 @@ protected:
bool _onoff;
};
class RecursionDepthCheck {
public:
RecursionDepthCheck(Codegen *cg, const AST::SourceLocation &loc)
: _cg(cg)
{
#ifdef QT_NO_DEBUG
const int depthLimit = 4000; // limit to ~1000 deep
#else
const int depthLimit = 1000; // limit to ~250 deep
#endif // QT_NO_DEBUG
++_cg->_recursionDepth;
if (_cg->_recursionDepth > depthLimit)
_cg->throwSyntaxError(loc, QStringLiteral("Maximum statement or expression depth exceeded"));
}
~RecursionDepthCheck()
{ --_cg->_recursionDepth; }
private:
Codegen *_cg;
};
int _recursionDepth = 0;
friend class RecursionDepthCheck;
private:
VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast);
void handleConstruct(const Reference &base, AST::ArgumentList *args);
};

View File

@ -50,6 +50,8 @@
#include <private/qqmlengine_p.h>
#include <private/qv4vme_moth_p.h>
#include <private/qv4module_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
#include "qv4compilationunitmapper_p.h"
#include <QQmlPropertyMap>
#include <QDateTime>
@ -167,6 +169,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
l->setter = QV4::Lookup::setterGeneric;
else if (type == CompiledData::Lookup::Type_GlobalGetter)
l->globalGetter = QV4::Lookup::globalGetterGeneric;
else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
l->nameIndex = compiledLookups[i].nameIndex;
}
}
@ -269,6 +273,24 @@ void CompilationUnit::unlink()
propertyCaches.clear();
if (runtimeLookups) {
for (uint i = 0; i < data->lookupTableSize; ++i) {
QV4::Lookup &l = runtimeLookups[i];
if (l.getter == QV4::QObjectWrapper::lookupGetter) {
if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
pc->release();
} else if (l.getter == QQmlValueTypeWrapper::lookupGetter) {
if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache)
pc->release();
}
if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) {
if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache)
pc->release();
}
}
}
dependentScripts.clear();
typeNameCache = nullptr;

View File

@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
#define QV4_DATA_STRUCTURE_VERSION 0x1c // Add trace slot to UPlus
#define QV4_DATA_STRUCTURE_VERSION 0x22 // Add trace slot to UPlus
class QIODevice;
class QQmlPropertyData;
@ -165,9 +165,10 @@ static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected
struct Lookup
{
enum Type : unsigned int {
Type_Getter = 0x0,
Type_Setter = 0x1,
Type_GlobalGetter = 2
Type_Getter = 0,
Type_Setter = 1,
Type_GlobalGetter = 2,
Type_QmlContextPropertyGetter = 3
};
union {
@ -292,29 +293,16 @@ struct Function
quint16_le nRegisters;
Location location;
// Qml Extensions Begin
// Array of resolved ID objects
size_t dependingIdObjectsOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
quint16_le nDependingIdObjects;
quint16_le nDependingContextProperties;
// Array of int pairs (property index and notify index)
size_t dependingContextPropertiesOffset() const { return dependingIdObjectsOffset() + nDependingIdObjects * sizeof(quint32); }
quint16_le nDependingScopeProperties;
// Array of int pairs (property index and notify index)
size_t dependingScopePropertiesOffset() const { return dependingContextPropertiesOffset() + nDependingContextProperties * sizeof(quint32); }
// Qml Extensions End
quint32_le nLabelInfos;
size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); }
typedef quint16_le TraceInfoCount;
TraceInfoCount nTraceInfos;
static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); }
quint32_le nLabelInfos;
size_t labelInfosOffset() const { return dependingScopePropertiesOffset() + nDependingScopeProperties; }
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
quint16 padding2;
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
@ -322,9 +310,6 @@ struct Function
const quint32_le *formalsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + formalsOffset); }
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast<const CodeOffsetToLine *>(reinterpret_cast<const char *>(this) + lineNumberOffset()); }
const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingIdObjectsOffset()); }
const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingContextPropertiesOffset()); }
const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + dependingScopePropertiesOffset()); }
// --- QQmlPropertyCacheCreator interface
const quint32_le *formalsBegin() const { return formalsTable(); }
@ -335,11 +320,9 @@ struct Function
const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int labelInfoSize, int codeSize) {
int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + labelInfoSize +
2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine);
static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) {
int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32)
+ nLines*sizeof(CodeOffsetToLine);
size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize);
Q_ASSERT(size < INT_MAX);
return int(size);
@ -349,7 +332,7 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
static_assert(sizeof(Function) == 60, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Method {
enum Type {

View File

@ -154,11 +154,6 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex)
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name)
{
return registerGlobalGetterLookup(registerString(name));
}
int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
@ -168,6 +163,15 @@ int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex)
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex)
{
CompiledData::Lookup l;
l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter;
l.nameIndex = nameIndex;
lookups << l;
return lookups.size() - 1;
}
int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *regexp)
{
CompiledData::RegExp re;
@ -423,28 +427,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->nTraceInfos = irFunction->nTraceInfos;
function->nRegisters = irFunction->registerCountInFunction;
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
function->nDependingScopeProperties = 0;
if (!irFunction->idObjectDependencies.isEmpty()) {
function->nDependingIdObjects = irFunction->idObjectDependencies.count();
Q_ASSERT(function->dependingIdObjectsOffset() == currentOffset);
currentOffset += function->nDependingIdObjects * sizeof(quint32);
}
if (!irFunction->contextObjectPropertyDependencies.isEmpty()) {
function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count();
Q_ASSERT(function->dependingContextPropertiesOffset() == currentOffset);
currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2;
}
if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) {
function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count();
Q_ASSERT(function->dependingScopePropertiesOffset() == currentOffset);
currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2;
}
if (!irFunction->labelInfo.empty()) {
function->nLabelInfos = quint32(irFunction->labelInfo.size());
Q_ASSERT(function->labelInfosOffset() == currentOffset);
@ -470,25 +452,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
// write line numbers
memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine));
// write QML dependencies
quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset());
for (int id : irFunction->idObjectDependencies) {
Q_ASSERT(id >= 0);
*writtenDeps++ = static_cast<quint32>(id);
}
writtenDeps = (quint32_le *)(f + function->dependingContextPropertiesOffset());
for (auto property : irFunction->contextObjectPropertyDependencies) {
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
}
writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset());
for (auto property : irFunction->scopeObjectPropertyDependencies) {
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
}
quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset());
for (unsigned u : irFunction->labelInfo) {
*labels++ = u;
@ -690,10 +653,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
Context *f = module->functions.at(i);
blockAndFunctionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
qmlIdDepsCount, qmlPropertyDepsCount, int(f->labelInfo.size()), f->code.size());
int(f->labelInfo.size()), f->code.size());
functionSize += size - f->code.size();
nextOffset += size;
}

View File

@ -118,8 +118,8 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
int registerGetterLookup(int nameIndex);
int registerSetterLookup(const QString &name);
int registerSetterLookup(int nameIndex);
int registerGlobalGetterLookup(const QString &name);
int registerGlobalGetterLookup(int nameIndex);
int registerQmlContextPropertyGetterLookup(int nameIndex);
int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp);

View File

@ -187,10 +187,13 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS
}
// ### can we relax the restrictions here?
if (c->contextType == ContextType::Eval || c->contextType == ContextType::Binding)
if (c->contextType == ContextType::Eval)
return result;
result.type = ResolvedName::Global;
if (c->contextType == ContextType::Binding)
result.type = ResolvedName::QmlGlobal;
else
result.type = ResolvedName::Global;
return result;
}

View File

@ -278,11 +278,6 @@ struct Context {
}
};
// Qml extension:
SmallSet<int> idObjectDependencies;
PropertyDependencyMap contextObjectPropertyDependencies;
PropertyDependencyMap scopeObjectPropertyDependencies;
Context(Context *parent, ContextType type)
: parent(parent)
, contextType(type)
@ -338,6 +333,7 @@ struct Context {
struct ResolvedName {
enum Type {
Unresolved,
QmlGlobal,
Global,
Local,
Stack,

View File

@ -57,7 +57,8 @@ using namespace QV4::Compiler;
using namespace QQmlJS::AST;
ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
: _cg(cg)
: QQmlJS::AST::Visitor(cg->recursionDepth())
, _cg(cg)
, _sourceCode(sourceCode)
, _context(nullptr)
, _allowFuncDecls(true)
@ -96,25 +97,6 @@ void ScanFunctions::leaveEnvironment()
_context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
}
bool ScanFunctions::preVisit(Node *ast)
{
if (_cg->hasError)
return false;
++_recursionDepth;
if (_recursionDepth > 1000) {
_cg->throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded"));
return false;
}
return true;
}
void ScanFunctions::postVisit(Node *)
{
--_recursionDepth;
}
void ScanFunctions::checkDirectivePrologue(StatementList *ast)
{
for (StatementList *it = ast; it; it = it->next) {
@ -893,3 +875,8 @@ void ScanFunctions::calcEscapingVariables()
}
}
}
void ScanFunctions::throwRecursionDepthError()
{
_cg->throwRecursionDepthError();
}

View File

@ -96,9 +96,6 @@ protected:
using Visitor::visit;
using Visitor::endVisit;
bool preVisit(AST::Node *ast) override;
void postVisit(AST::Node *) override;
void checkDirectivePrologue(AST::StatementList *ast);
void checkName(const QStringRef &name, const AST::SourceLocation &loc);
@ -160,6 +157,8 @@ protected:
bool visit(AST::WithStatement *ast) override;
void endVisit(AST::WithStatement *ast) override;
void throwRecursionDepthError() override;
protected:
bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
@ -173,8 +172,6 @@ protected:
bool _allowFuncDecls;
ContextType defaultProgramType;
unsigned _recursionDepth = 0;
private:
static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
};

View File

@ -287,6 +287,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index << TRACE_SLOT;
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
d << index << TRACE_SLOT;
MOTH_END_INSTR(LoadQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(StoreNameSloppy)
d << name;
MOTH_END_INSTR(StoreNameSloppy)
@ -328,26 +332,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(property, nFormals);
MOTH_END_INSTR(StoreSuperProperty)
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
MOTH_END_INSTR(StoreScopeObjectProperty)
MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
MOTH_END_INSTR(LoadScopeObjectProperty)
MOTH_BEGIN_INSTR(StoreContextObjectProperty)
d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
MOTH_END_INSTR(StoreContextObjectProperty)
MOTH_BEGIN_INSTR(LoadContextObjectProperty)
d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)");
MOTH_END_INSTR(LoadContextObjectProperty)
MOTH_BEGIN_INSTR(LoadIdObject)
d << dumpRegister(base, nFormals) << "[" << index << "]";
MOTH_END_INSTR(LoadIdObject)
MOTH_BEGIN_INSTR(Yield)
MOTH_END_INSTR(Yield)
@ -394,15 +378,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(CallScopeObjectProperty)
d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
<< TRACE_SLOT;
MOTH_END_INSTR(CallScopeObjectProperty)
MOTH_BEGIN_INSTR(CallContextObjectProperty)
d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals)
<< TRACE_SLOT;
MOTH_END_INSTR(CallContextObjectProperty)
MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT;
MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals)
@ -730,14 +708,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << index;
MOTH_END_INSTR(GetTemplateObject)
MOTH_BEGIN_INSTR(LoadQmlContext)
d << dumpRegister(result, nFormals);
MOTH_END_INSTR(LoadQmlContext)
MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
d << dumpRegister(result, nFormals);
MOTH_END_INSTR(LoadQmlImportedScripts)
MOTH_BEGIN_INSTR(TailCall)
d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(TailCall)

View File

@ -86,12 +86,11 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value)
#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot)
#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot)
#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot)
#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name)
#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name)
#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot)
#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot)
#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired)
#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired)
#define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base)
#define INSTR_Yield(op) INSTRUCTION(op, Yield, 0)
#define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0)
@ -101,8 +100,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base)
#define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property)
#define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property)
#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex)
#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex)
#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot)
#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot)
#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot)
@ -113,8 +110,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot)
#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot)
#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot)
#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 5, name, base, argc, argv, traceSlot)
#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 5, name, base, argc, argv, traceSlot)
#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot)
#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot)
#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
@ -194,7 +190,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot)
#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot)
#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
#define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count)
#define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0)
@ -228,6 +223,7 @@ QT_BEGIN_NAMESPACE
F(LoadClosure) \
F(LoadName) \
F(LoadGlobalLookup) \
F(LoadQmlContextPropertyLookup) \
F(StoreNameSloppy) \
F(StoreNameStrict) \
F(LoadElement) \
@ -238,11 +234,6 @@ QT_BEGIN_NAMESPACE
F(SetLookup) \
F(LoadSuperProperty) \
F(StoreSuperProperty) \
F(StoreScopeObjectProperty) \
F(StoreContextObjectProperty) \
F(LoadScopeObjectProperty) \
F(LoadContextObjectProperty) \
F(LoadIdObject) \
F(ConvertThisToObject) \
F(ToObject) \
F(Jump) \
@ -296,8 +287,7 @@ QT_BEGIN_NAMESPACE
F(CallName) \
F(CallPossiblyDirectEval) \
F(CallGlobalLookup) \
F(CallScopeObjectProperty) \
F(CallContextObjectProperty) \
F(CallQmlContextPropertyLookup) \
F(CallWithSpread) \
F(Construct) \
F(ConstructWithSpread) \
@ -328,8 +318,6 @@ QT_BEGIN_NAMESPACE
F(CreateMappedArgumentsObject) \
F(CreateUnmappedArgumentsObject) \
F(CreateRestParameter) \
F(LoadQmlContext) \
F(LoadQmlImportedScripts) \
F(Yield) \
F(YieldStar) \
F(Resume) \

View File

@ -142,9 +142,10 @@ types:
\li qmlRegisterType() (with no parameters) registers a C++ type that is not
instantiable and cannot be referred to from QML. This enables the engine to
coerce any inherited types that are instantiable from QML.
\li qmlRegisterInterface() registers a Qt interface type with a specific QML
type name. The type is not instantiable from QML but can be referred to by its
type name.
\li qmlRegisterInterface() registers an existing Qt interface type. The type is
not instantiable from QML, and you cannot declare QML properties with it. Using
C++ properties of this type from QML will do the expected interface casts,
though.
\li qmlRegisterUncreatableType() registers a named C++ type that is not
instantiable but should be identifiable as a type to the QML type system. This
is useful if a type's enums or attached properties should be accessible from QML

View File

@ -55,6 +55,8 @@
\li decodeURIComponent(encodedURIComponent)
\li encodeURI(uri)
\li encodeURIComponent(uriComponent)
\li escape(string)
\li unescape(string)
\endlist
\section2 Constructor Properties
@ -63,11 +65,20 @@
\li Object
\li Function
\li Array
\li ArrayBuffer
\li String
\li Boolean
\li Number
\li DataView
\li Date
\li Promise
\li RegExp
\li Map
\li WeakMap
\li Set
\li WeakSet
\li SharedArrayBuffer
\li Symbol
\li Error
\li EvalError
\li RangeError
@ -80,8 +91,11 @@
\section2 Other Properties
\list
\li Atomics
\li Math
\li JSON
\li Reflect
\li Proxy
\endlist
\section1 The Object Object
@ -92,12 +106,19 @@
\list
\li getPrototypeOf(O)
\li setPrototypeOf(O, P)
\li getOwnPropertyDescriptor(O, P)
\li getOwnPropertyDescriptors(O)
\li getOwnPropertyNames(O)
\li getOwnPropertySymbols(O)
\li assign(O [, Properties])
\li create(O [, Properties])
\li defineProperty(O, P, Attributes)
\li defineProperties(O, Properties)
\li entries(O)
\li is(V1, V2)
\li keys(O)
\li values(O)
\li seal(O)
\li isSealed(O)
\li freeze(O)
@ -117,6 +138,8 @@
\li hasOwnProperty(V)
\li isPrototypeOf(V)
\li propertyIsEnumerable(V)
\li __defineGetter__(P, F)
\li __defineSetter__(P, F)
\endlist
\section1 Function Objects
@ -130,6 +153,7 @@
\li apply(thisArg, argArray)
\li call(thisArg [, arg1 [, arg2, ...]])
\li bind((thisArg [, arg1 [, arg2, …]])
\li [Symbol.hasInstance](O)
\endlist
\section1 Array Objects
@ -142,9 +166,14 @@
\li toString()
\li toLocaleString()
\li concat([item1 [, item2 [, ...]]])
\li copyWithin([item1 [, item2 [, ...]]])
\li entries()
\li fill(item [, index1 [, index2]])
\li join(separator)
\li find(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9
\li findIndex(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9
\li includes(item)
\li keys()
\li pop()
\li push([item1 [, item2 [, ...]]])
\li reverse()
@ -162,6 +191,8 @@
\li filter(callbackfn [, thisArg])
\li reduce(callbackfn [, initialValue])
\li reduceRight(callbackfn [, initialValue])
\li values()
\li [Symbol.iterator]()
\endlist
\section1 String Objects
@ -175,6 +206,7 @@
\li valueOf()
\li charAt(pos)
\li charCodeAt(pos)
\li codePointAt(pos)
\li concat([string1 [, string2 [, ...]]])
\li endsWith(searchString [, endPosition ]) // ECMAScript 6: Added in Qt 5.8
\li includes(searchString [, position ]) // ECMAScript 6: Added in 5.8
@ -182,18 +214,23 @@
\li lastIndexOf(searchString, position)
\li localeCompare(that)
\li match(regexp)
\li normalize()
\li padEnd(length [, string])
\li padStart(length [, string])
\li repeat(count) // ECMAScript 6: Added in Qt 5.9
\li replace(searchValue, replaceValue)
\li search(regexp)
\li slice(start, end)
\li split(separator, limit)
\li startsWith(searchString [, position ]) // ECMAScript 6: Added in Qt 5.8
\li substr(start, length)
\li substring(start, end)
\li toLowerCase()
\li toLocaleLowerCase()
\li toUpperCase()
\li toLocaleUpperCase()
\li trim()
\li [Symbol.iterator]()
\endlist
Additionally, the QML engine adds the following functions to the \l String prototype:
@ -222,6 +259,7 @@
\list
\li toString(radix)
\li toLocaleString()
\li valueOf()
\li toFixed(fractionDigits)
\li toExponential(fractionDigits)
\li toPrecision(precision)
@ -245,12 +283,16 @@
\li MAX_VALUE
\li MIN_VALUE
\li EPSILON // ECMAScript 6: Added in Qt 5.8
\li MAX_SAFE_INTEGER
\li MIN_SAFE_INTEGER
\endlist
\section3 Function Properties
\list
\li isFinite(x) // ECMAScript 6: Added in Qt 5.8
\li isInteger(x)
\li isSafeInteger(x)
\li isNaN(x) // ECMAScript 6: Added in Qt 5.8
\endlist
@ -274,14 +316,27 @@
\list
\li abs(x)
\li acos(x)
\li acosh(x)
\li asin(x)
\li asinh(x)
\li atan(x)
\li atanh(x)
\li atan2(y, x)
\li cbrt(x)
\li ceil(x)
\li clz32(x)
\li cos(x)
\li cosh(x)
\li exp(x)
\li expm1(x)
\li floor(x)
\li fround(x)
\li hypot(x, y)
\li imul(x, y)
\li log(x)
\li log10(x)
\li log1p(x)
\li log2(x)
\li max([value1 [, value2 [, ...]]])
\li min([value1 [, value2 [, ...]]])
\li pow(x, y)
@ -289,8 +344,11 @@
\li round(x)
\li sign(x) // ECMAScript 6: Added in Qt 5.8
\li sin(x)
\li sinh(x)
\li sqrt(x)
\li tan(x)
\li tanh(x)
\li trunc(x)
\endlist
\section1 Date Objects
@ -338,11 +396,14 @@
\li setUTCDate(date)
\li setMonth(month [, date])
\li setUTCMonth(month [, date])
\li setYear(year)
\li setFullYear(year [, month [, date]])
\li setUTCFullYear(year [, month [, date]])
\li toUTCString()
\li toGMTString()
\li toISOString()
\li toJSON()
\li [Symbol.toPrimitive](hint)
\endlist
Additionally, the QML engine adds the following functions to the \l Date prototype:

View File

@ -40,11 +40,10 @@ not provide a \c window object or \c{DOM API} as commonly found in a browser env
Like a browser or server-side JavaScript environment, the QML runtime implements the
\l{ECMA-262}{ECMAScript Language Specification} standard. This provides access to
all of the built-in types and functions defined by the standard, such as Object, Array, and Math.
The QML runtime implements the 5th edition of the standard, which is the same edition commonly
implemented by browsers.
The QML runtime implements the 7th edition of the standard.
The standard ECMAScript built-ins are not explicitly documented in the QML documentation. For more
information on their use, please refer to the ECMA-262 5th edition standard or one of the many online
information on their use, please refer to the ECMA-262 7th edition standard or one of the many online
JavaScript reference and tutorial sites, such as the \l{W3Schools JavaScript Reference} (JavaScript Objects
Reference section). Many sites focus on JavaScript in the browser, so in some cases you may need to double
check the specification to determine whether a given function or object is part of standard ECMAScript or

View File

@ -27,7 +27,7 @@
/*!
\qmlmodule QtQml.StateMachine 1.\QtMinorVersion
\title Declarative State Machine QML Types
\title Qt QML State Machine QML Types
\brief Provides QML types to create and execute state graphs.
The following is a list of QML types provided by the module:
@ -322,7 +322,7 @@
\section1 Related Information
\list
\li \l{Declarative State Machine QML Types}
\li \l{Qt QML State Machine QML Types}
\li \l{The State Machine Framework}
\endlist
*/

View File

@ -208,17 +208,20 @@ public:
isNumber.link(this);
}
// this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
load64(lhs, lhsTarget);
urshift64(lhsTarget, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
auto lhsIsInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
pushAligned(AccumulatorRegister);
const Address accumulatorStackAddress(JSStackFrameRegister,
offsetof(CallData, accumulator));
storeAccumulator(accumulatorStackAddress);
move(lhsTarget, registerForArg(0));
callHelper(toInt32Helper);
move(ReturnValueRegister, lhsTarget);
popAligned(AccumulatorRegister);
loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);
urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
@ -498,6 +501,7 @@ public:
isNumber.link(this);
}
// this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
@ -510,32 +514,28 @@ public:
auto lhsIsInt = jump();
lhsIsNotInt.link(this);
if (accumulatorNeedsSaving) {
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
}
// Save accumulator from being garbage collected, no matter if we will reuse the register.
const Address accumulatorStackAddress(JSStackFrameRegister,
offsetof(CallData, accumulator));
storeAccumulator(accumulatorStackAddress);
if (ArgInRegCount < 2) {
if (!accumulatorNeedsSaving)
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
push(lhsTarget);
load32(lhs, lhsTarget);
push(lhsTarget);
} else {
if (accumulatorNeedsSaving)
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
move(lhsTarget, registerForArg(1));
load32(lhs, registerForArg(0));
}
callHelper(toInt32Helper);
move(ReturnValueRegisterValue, lhsTarget);
if (accumulatorNeedsSaving) {
addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
pop(AccumulatorRegisterValue);
pop(AccumulatorRegisterTag);
} else if (ArgInRegCount < 2) {
if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
}
if (accumulatorNeedsSaving) // otherwise it's still the same
loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);

View File

@ -213,6 +213,14 @@ void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/)
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/)
{
as->prepareCallWithArgCount(2);
as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_StoreNameSloppy(int name)
{
STORE_IP();
@ -329,61 +337,6 @@ void BaselineJIT::generate_StoreSuperProperty(int property)
BASELINEJIT_GENERATE_RUNTIME_CALL(StoreSuperProperty, CallResultDestination::Ignore);
}
void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex)
{
STORE_ACC();
as->prepareCallWithArgCount(4);
as->passAccumulatorAsArg(3);
as->passInt32AsArg(propertyIndex, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlScopeObjectProperty, CallResultDestination::Ignore);
}
void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex)
{
STORE_ACC();
as->prepareCallWithArgCount(4);
as->passAccumulatorAsArg(3);
as->passInt32AsArg(propertyIndex, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(StoreQmlContextObjectProperty, CallResultDestination::Ignore);
}
void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired)
{
STORE_IP();
as->prepareCallWithArgCount(4);
as->passInt32AsArg(captureRequired, 3);
as->passInt32AsArg(propertyIndex, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlScopeObjectProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired)
{
STORE_IP();
as->prepareCallWithArgCount(4);
as->passInt32AsArg(captureRequired, 3);
as->passInt32AsArg(propertyIndex, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContextObjectProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_LoadIdObject(int index, int base)
{
STORE_IP();
as->prepareCallWithArgCount(3);
as->passInt32AsArg(index, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlIdObject, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_Yield()
{
// #####
@ -493,31 +446,18 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /
BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/)
void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
int /*traceSlot*/)
{
STORE_IP();
as->prepareCallWithArgCount(5);
as->passInt32AsArg(argc, 4);
as->passJSSlotAsArg(argv, 3);
as->passInt32AsArg(propIdx, 2);
as->passJSSlotAsArg(base, 1);
as->prepareCallWithArgCount(4);
as->passInt32AsArg(argc, 3);
as->passJSSlotAsArg(argv, 2);
as->passInt32AsArg(index, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlScopeObjectProperty, CallResultDestination::InAccumulator);
BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int /*traceSlot*/)
{
STORE_IP();
as->prepareCallWithArgCount(5);
as->passInt32AsArg(argc, 4);
as->passJSSlotAsArg(argv, 3);
as->passInt32AsArg(propIdx, 2);
as->passJSSlotAsArg(base, 1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextObjectProperty, CallResultDestination::InAccumulator);
}
void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/)
{
STORE_IP();
@ -937,22 +877,6 @@ void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); }
// as->checkException();
//}
void BaselineJIT::generate_LoadQmlContext(int result)
{
as->prepareCallWithArgCount(1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlContext, CallResultDestination::InAccumulator);
as->storeReg(result);
}
void BaselineJIT::generate_LoadQmlImportedScripts(int result)
{
as->prepareCallWithArgCount(1);
as->passEngineAsArg(0);
BASELINEJIT_GENERATE_RUNTIME_CALL(LoadQmlImportedScripts, CallResultDestination::InAccumulator);
as->storeReg(result);
}
void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
as->loadValue(Value::emptyValue().rawValue());

View File

@ -97,6 +97,7 @@ public:
void generate_LoadClosure(int value) override;
void generate_LoadName(int name, int traceSlot) override;
void generate_LoadGlobalLookup(int index, int traceSlot) override;
void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override;
void generate_StoreNameSloppy(int name) override;
void generate_StoreNameStrict(int name) override;
void generate_LoadElement(int base, int traceSlot) override;
@ -107,15 +108,6 @@ public:
void generate_SetLookup(int index, int base) override;
void generate_LoadSuperProperty(int property) override;
void generate_StoreSuperProperty(int property) override;
void generate_StoreScopeObjectProperty(int base,
int propertyIndex) override;
void generate_StoreContextObjectProperty(int base,
int propertyIndex) override;
void generate_LoadScopeObjectProperty(int propertyIndex, int base,
int captureRequired) override;
void generate_LoadContextObjectProperty(int propertyIndex, int base,
int captureRequired) override;
void generate_LoadIdObject(int index, int base) override;
void generate_Yield() override;
void generate_YieldStar() override;
void generate_Resume(int) override;
@ -128,8 +120,7 @@ public:
void generate_CallName(int name, int argc, int argv, int traceSlot) override;
void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override;
void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override;
void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override;
void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv, int traceSlot) override;
void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override;
void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override;
void generate_TailCall(int func, int thisObject, int argc, int argv) override;
void generate_Construct(int func, int argc, int argv) override;
@ -211,8 +202,6 @@ public:
void generate_Div(int lhs) override;
void generate_Mod(int lhs, int traceSlot) override;
void generate_Sub(int lhs, int traceSlot) override;
void generate_LoadQmlContext(int result) override;
void generate_LoadQmlImportedScripts(int result) override;
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;

View File

@ -800,45 +800,10 @@ void GraphBuilder::generate_StoreSuperProperty(int property)
env()->accumulator());
}
void GraphBuilder::generate_StoreScopeObjectProperty(int base, int propertyIndex)
void GraphBuilder::generate_LoadQmlContextPropertyLookup(int propertyIndex, int /*traceSlot*/)
{
createNode(opBuilder()->get<Meta::QMLStoreScopeObjectProperty>(),
env()->slot(base),
createConstant(propertyIndex),
env()->accumulator());
}
void GraphBuilder::generate_StoreContextObjectProperty(int base, int propertyIndex)
{
createNode(opBuilder()->get<Meta::QMLStoreContextObjectProperty>(),
env()->slot(base),
createConstant(propertyIndex),
env()->accumulator());
}
void GraphBuilder::generate_LoadScopeObjectProperty(int propertyIndex, int base,
int captureRequired)
{
bindAcc(createNode(opBuilder()->get<Meta::QMLLoadScopeObjectProperty>(),
env()->slot(base),
createConstant(propertyIndex),
createConstant(captureRequired)));
}
void GraphBuilder::generate_LoadContextObjectProperty(int propertyIndex, int base,
int captureRequired)
{
bindAcc(createNode(opBuilder()->get<Meta::QMLLoadContextObjectProperty>(),
env()->slot(base),
createConstant(propertyIndex),
createConstant(captureRequired)));
}
void GraphBuilder::generate_LoadIdObject(int index, int base)
{
bindAcc(createNode(opBuilder()->get<Meta::QMLLoadIdObject>(),
env()->slot(base),
createConstant(index)));
bindAcc(createNode(opBuilder()->get<Meta::QMLLoadQmlContextPropertyLookup>(),
createConstant(propertyIndex)));
}
void GraphBuilder::generate_Yield() { Q_UNREACHABLE(); }
@ -913,22 +878,12 @@ void GraphBuilder::generate_CallGlobalLookup(int index, int argc, int argv, int
finalizeCall(Meta::JSCallGlobalLookup, args, argc, argv);
}
void GraphBuilder::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv,
int /*traceSlot*/)
void GraphBuilder::generate_CallQmlContextPropertyLookup(int index, int argc, int argv,
int /*traceSlot*/)
{
VarArgNodes args;
args.append(env()->slot(base));
args.append(createConstant(propIdx));
finalizeCall(Meta::QMLCallScopeObjectProperty, args, argc, argv);
}
void GraphBuilder::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv,
int /*traceSlot*/)
{
VarArgNodes args;
args.append(env()->slot(base));
args.append(createConstant(propIdx));
finalizeCall(Meta::QMLCallContextObjectProperty, args, argc, argv);
args.append(createConstant(index));
finalizeCall(Meta::QMLCallQmlContextPropertyLookup, args, argc, argv);
}
void GraphBuilder::generate_SetUnwindHandler(int offset)
@ -1636,17 +1591,6 @@ void GraphBuilder::generate_Sub(int lhs, int /*traceSlot*/)
env()->accumulator()));
}
void GraphBuilder::generate_LoadQmlContext(int result)
{
env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadContext>()), result);
}
void GraphBuilder::generate_LoadQmlImportedScripts(int result)
{
env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::QMLLoadImportedScripts>()),
result);
}
void GraphBuilder::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
{
for (int reg = firstReg; reg < firstReg + count; ++reg)

View File

@ -173,15 +173,7 @@ protected: // ByteCodeHandler
void generate_SetLookup(int index, int base) override;
void generate_LoadSuperProperty(int property) override;
void generate_StoreSuperProperty(int property) override;
void generate_StoreScopeObjectProperty(int base,
int propertyIndex) override;
void generate_StoreContextObjectProperty(int base,
int propertyIndex) override;
void generate_LoadScopeObjectProperty(int propertyIndex, int base,
int captureRequired) override;
void generate_LoadContextObjectProperty(int propertyIndex, int base,
int captureRequired) override;
void generate_LoadIdObject(int index, int base) override;
void generate_LoadQmlContextPropertyLookup(int property, int traceSlot) override;
void generate_Yield() override;
void generate_YieldStar() override;
void generate_Resume(int offset) override;
@ -196,10 +188,7 @@ protected: // ByteCodeHandler
void generate_CallName(int name, int argc, int argv, int traceSlot) override;
void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override;
void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override;
void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv,
int traceSlot) override;
void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv,
int traceSlot) override;
void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override;
void generate_SetUnwindHandler(int offset) override;
void generate_UnwindDispatch() override;
void generate_UnwindToLabel(int level, int offset) override;
@ -284,8 +273,6 @@ protected: // ByteCodeHandler
void generate_Div(int lhs) override;
void generate_Mod(int lhs, int traceSlot) override;
void generate_Sub(int lhs, int traceSlot) override;
void generate_LoadQmlContext(int result) override;
void generate_LoadQmlImportedScripts(int result) override;
void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void generate_ThrowOnNullOrUndefined() override;
void generate_GetTemplateObject(int index) override;

View File

@ -223,11 +223,7 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat
case K::JSDeleteName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSIn: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSInstanceOf: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::QMLLoadScopeObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::QMLStoreScopeObjectProperty: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow);
case K::QMLLoadContextObjectProperty: return get(3, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::QMLStoreContextObjectProperty: return get(3, 1, 1, 1, 1, 2, none, F::CanThrow);
case K::QMLLoadIdObject: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::QMLLoadQmlContextPropertyLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
case K::JSGreaterThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow);
@ -263,8 +259,6 @@ inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *stat
case K::JSTypeofValue: return get(1, 0, 0, 1, 0, 0, any, F::Pure);
case K::JSDeclareVar: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::JSDestructureRestElement: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow);
case K::QMLLoadContext: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags);
case K::QMLLoadImportedScripts: return get(0, 0, 0, 1, 0, 0, any, F::NoFlags);
case K::JSCreateCallContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags);
case K::JSCreateCatchContext: return get(2, 1, 1, 1, 1, 1, none, F::NoFlags);
@ -518,18 +512,12 @@ static ReturnValue operateOnRuntimeCall(Operation::Kind kind, bool abortOnMissin
case K::JSDeleteName: return M<R::DeleteName>::doIt();
case K::JSIn: return M<R::In>::doIt();
case K::JSInstanceOf: return M<R::Instanceof>::doIt();
case K::QMLLoadScopeObjectProperty: return M<R::LoadQmlScopeObjectProperty>::doIt();
case K::QMLStoreScopeObjectProperty: return M<R::StoreQmlScopeObjectProperty>::doIt();
case K::QMLLoadContextObjectProperty: return M<R::LoadQmlContextObjectProperty>::doIt();
case K::QMLStoreContextObjectProperty: return M<R::StoreQmlContextObjectProperty>::doIt();
case K::QMLLoadIdObject: return M<R::LoadQmlIdObject>::doIt();
case K::QMLLoadQmlContextPropertyLookup: return M<R::LoadQmlContextPropertyLookup>::doIt();
case K::JSTypeofName: return M<R::TypeofName>::doIt();
case K::JSTypeofValue: return M<R::TypeofValue>::doIt();
case K::JSDeclareVar: return M<R::DeclareVar>::doIt();
case K::JSDestructureRestElement: return M<R::DestructureRestElement>::doIt();
case K::QMLLoadContext: return M<R::LoadQmlContext>::doIt();
case K::QMLLoadImportedScripts: return M<R::LoadQmlImportedScripts>::doIt();
case K::JSThisToObject: return M<R::ConvertThisToObject>::doIt();
case K::JSCreateMappedArgumentsObject: return M<R::CreateMappedArgumentsObject>::doIt();
case K::JSCreateUnmappedArgumentsObject: return M<R::CreateUnmappedArgumentsObject>::doIt();

View File

@ -123,12 +123,10 @@ enum OpKind: uint16_t {
JSDeleteName,
JSIn,
JSInstanceOf,
/* ok, these are qml object ops, but we don't care for now and treat them a s JS */
QMLLoadScopeObjectProperty,
QMLStoreScopeObjectProperty,
QMLLoadContextObjectProperty,
QMLStoreContextObjectProperty,
QMLLoadIdObject,
/* ok, these are qml object ops, but we don't care for now and treat them as JS */
QMLLoadQmlContextPropertyLookup,
QMLCallQmlContextPropertyLookup,
JSEqual,
JSGreaterThan,
@ -168,16 +166,11 @@ enum OpKind: uint16_t {
JSCreateClass,
JSConstruct,
JSConstructWithSpread,
/* ok, these are qml vararg calls, but we don't care for now and treat them as JS */
QMLCallScopeObjectProperty,
QMLCallContextObjectProperty,
JSTypeofName,
JSTypeofValue,
JSDeclareVar,
JSDestructureRestElement,
QMLLoadContext,
QMLLoadImportedScripts,
JSThisToObject,
JSCreateMappedArgumentsObject,
JSCreateUnmappedArgumentsObject,

View File

@ -1189,6 +1189,14 @@ ReturnedValue ExecutionEngine::throwTypeError(const QString &message)
return throwError(error);
}
ReturnedValue ExecutionEngine::throwReferenceError(const QString &name)
{
Scope scope(this);
QString msg = name + QLatin1String(" is not defined");
ScopedObject error(scope, newReferenceErrorObject(msg));
return throwError(error);
}
ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
{
Scope scope(this);

View File

@ -572,6 +572,7 @@ public:
ReturnedValue throwTypeError();
ReturnedValue throwTypeError(const QString &message);
ReturnedValue throwReferenceError(const Value &value);
ReturnedValue throwReferenceError(const QString &name);
ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column);
ReturnedValue throwRangeError(const Value &value);
ReturnedValue throwRangeError(const QString &message);

View File

@ -96,7 +96,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
, codeData(function->code())
, jittedCode(nullptr)
, codeRef(nullptr)
, hasQmlDependencies(function->hasQmlDependencies())
{
Scope scope(engine);
Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));

View File

@ -94,7 +94,6 @@ public:
Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
bool hasQmlDependencies;
bool isEval = false;
static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);

View File

@ -257,11 +257,15 @@ void InternalClass::init(Heap::InternalClass *other)
void InternalClass::destroy()
{
#ifndef QT_NO_DEBUG
for (const auto &t : transitions) {
Q_ASSERT(!t.lookup || !t.lookup->isMarked());
}
if (t.lookup) {
#ifndef QT_NO_DEBUG
Q_ASSERT(t.lookup->parent == this);
#endif
t.lookup->parent = nullptr;
}
}
if (parent && parent->engine && parent->isMarked())
parent->removeChildEntry(this);
@ -659,8 +663,6 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
if (ic->prototype)
ic->prototype->mark(stack);
if (ic->parent)
ic->parent->mark(stack);
ic->nameMap.mark(stack);
}

View File

@ -69,37 +69,7 @@ void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
Heap::Object *obj = object->d();
PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
if (name.isArrayIndex()) {
indexedLookup.index = name.asArrayIndex();
getter = getterIndexed;
return getter(this, engine, *object);
}
auto index = obj->internalClass->findValueOrGetter(name);
if (index.isValid()) {
PropertyAttributes attrs = index.attrs;
uint nInline = obj->vtable()->nInlineProperties;
if (attrs.isData()) {
if (index.index < obj->vtable()->nInlineProperties) {
index.index += obj->vtable()->inlinePropertyOffset;
getter = getter0Inline;
} else {
index.index -= nInline;
getter = getter0MemberData;
}
} else {
getter = getterAccessor;
}
objectLookup.ic = obj->internalClass;
objectLookup.offset = index.index;
return getter(this, engine, *object);
}
protoLookup.protoId = obj->internalClass->protoId;
resolveProtoGetter(name, obj->prototype());
return getter(this, engine, *object);
return object->resolveLookupGetter(engine, this);
}
ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object)
@ -409,7 +379,7 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->primitiveLookup.type) {
if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
@ -420,7 +390,7 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c
ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->primitiveLookup.type) {
if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
@ -473,56 +443,7 @@ ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engi
bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
{
Scope scope(engine);
ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
Heap::InternalClass *c = object->internalClass();
PropertyKey key = name->toPropertyKey();
auto idx = c->findValueOrSetter(key);
if (idx.isValid()) {
if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
Q_ASSERT(!idx.attrs.isAccessor());
setter = arrayLengthSetter;
return setter(this, engine, *object, value);
} else if (idx.attrs.isData() && idx.attrs.isWritable()) {
objectLookup.ic = object->internalClass();
objectLookup.index = idx.index;
const auto nInline = object->d()->vtable()->nInlineProperties;
if (idx.index < nInline) {
setter = Lookup::setter0Inline;
objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
} else {
setter = Lookup::setter0MemberData;
objectLookup.offset = idx.index - nInline;
}
return setter(this, engine, *object, value);
} else {
// ### handle setter
setter = setterFallback;
}
return setter(this, engine, *object, value);
}
insertionLookup.protoId = c->protoId;
if (!object->put(key, value)) {
setter = Lookup::setterFallback;
return false;
}
if (object->internalClass() == c) {
// ### setter in the prototype, should handle this
setter = setterFallback;
return true;
}
idx = object->internalClass()->findValueOrSetter(key);
if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
setter = setterFallback;
return false;
}
insertionLookup.newClass = object->internalClass();
insertionLookup.offset = idx.index;
setter = setterInsert;
return true;
return object->resolveLookupSetter(engine, this, value);
}
bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)

View File

@ -68,8 +68,10 @@ struct Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject);
bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v);
};
// NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null
union {
struct {
Heap::Base *h1;
@ -119,6 +121,39 @@ struct Lookup {
uint index;
uint unused;
} indexedLookup;
struct {
Heap::InternalClass *ic;
Heap::QObjectWrapper *staticQObject;
QQmlPropertyCache *propertyCache;
QQmlPropertyData *propertyData;
} qobjectLookup;
struct {
Heap::InternalClass *ic;
quintptr unused;
QQmlPropertyCache *propertyCache;
QQmlPropertyData *propertyData;
} qgadgetLookup;
struct {
quintptr unused1;
quintptr unused2;
int scriptIndex;
} qmlContextScriptLookup;
struct {
Heap::Object *singleton;
quintptr unused;
} qmlContextSingletonLookup;
struct {
quintptr unused1;
quintptr unused2;
int objectId;
} qmlContextIdObjectLookup;
struct {
// Same as protoLookup, as used for global lookups
quintptr reserved1;
quintptr reserved2;
quintptr reserved3;
ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine);
} qmlContextGlobalLookup;
};
uint nameIndex;

View File

@ -320,8 +320,11 @@ bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
if (arrayIndex != UINT_MAX && o->arrayData()) {
if (!arrayIndex)
arrayNode = o->sparseBegin();
SparseArrayNode *arrayNode = nullptr;
if (o->arrayType() == Heap::ArrayData::Sparse) {
SparseArray *sparse = o->arrayData()->sparse;
arrayNode = arrayIndex ? sparse->lowerBound(arrayIndex) : sparse->begin();
}
// sparse arrays
if (arrayNode) {
@ -339,7 +342,6 @@ PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, Pr
*attrs = a;
return PropertyKey::fromArrayIndex(k);
}
arrayNode = nullptr;
arrayIndex = UINT_MAX;
}
// dense arrays
@ -734,6 +736,95 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v
return checkedInstanceOf(engine, function, var);
}
ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
{
Heap::Object *obj = object->d();
PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
if (name.isArrayIndex()) {
lookup->indexedLookup.index = name.asArrayIndex();
lookup->getter = Lookup::getterIndexed;
return lookup->getter(lookup, engine, *object);
}
auto index = obj->internalClass->findValueOrGetter(name);
if (index.isValid()) {
PropertyAttributes attrs = index.attrs;
uint nInline = obj->vtable()->nInlineProperties;
if (attrs.isData()) {
if (index.index < obj->vtable()->nInlineProperties) {
index.index += obj->vtable()->inlinePropertyOffset;
lookup->getter = Lookup::getter0Inline;
} else {
index.index -= nInline;
lookup->getter = Lookup::getter0MemberData;
}
} else {
lookup->getter = Lookup::getterAccessor;
}
lookup->objectLookup.ic = obj->internalClass;
lookup->objectLookup.offset = index.index;
return lookup->getter(lookup, engine, *object);
}
lookup->protoLookup.protoId = obj->internalClass->protoId;
lookup->resolveProtoGetter(name, obj->prototype());
return lookup->getter(lookup, engine, *object);
}
bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
{
Scope scope(engine);
ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
Heap::InternalClass *c = object->internalClass();
PropertyKey key = name->toPropertyKey();
auto idx = c->findValueOrSetter(key);
if (idx.isValid()) {
if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
Q_ASSERT(!idx.attrs.isAccessor());
lookup->setter = Lookup::arrayLengthSetter;
return lookup->setter(lookup, engine, *object, value);
} else if (idx.attrs.isData() && idx.attrs.isWritable()) {
lookup->objectLookup.ic = object->internalClass();
lookup->objectLookup.index = idx.index;
const auto nInline = object->d()->vtable()->nInlineProperties;
if (idx.index < nInline) {
lookup->setter = Lookup::setter0Inline;
lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
} else {
lookup->setter = Lookup::setter0MemberData;
lookup->objectLookup.offset = idx.index - nInline;
}
return lookup->setter(lookup, engine, *object, value);
} else {
// ### handle setter
lookup->setter = Lookup::setterFallback;
}
return lookup->setter(lookup, engine, *object, value);
}
lookup->insertionLookup.protoId = c->protoId;
if (!object->put(key, value)) {
lookup->setter = Lookup::setterFallback;
return false;
}
if (object->internalClass() == c) {
// ### setter in the prototype, should handle this
lookup->setter = Lookup::setterFallback;
return true;
}
idx = object->internalClass()->findValueOrSetter(key);
if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
lookup->setter = Lookup::setterFallback;
return false;
}
lookup->insertionLookup.newClass = object->internalClass();
lookup->insertionLookup.offset = idx.index;
lookup->setter = Lookup::setterInsert;
return true;
}
ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
{
Scope scope(engine);

View File

@ -375,6 +375,11 @@ public:
bool setProtoFromNewTarget(const Value *newTarget);
ReturnedValue resolveLookupGetter(ExecutionEngine *engine, Lookup *lookup) const
{ return vtable()->resolveLookupGetter(this, engine, lookup); }
ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value)
{ return vtable()->resolveLookupSetter(this, engine, lookup, value); }
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
@ -389,6 +394,8 @@ protected:
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static qint64 virtualGetLength(const Managed *m);
static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
public:
// qv4runtime uses this directly
static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var);
@ -408,7 +415,6 @@ struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
uint arrayIndex = 0;
uint memberIndex = 0;
bool iterateOverSymbols = false;
SparseArrayNode *arrayNode = nullptr;
~ObjectOwnPropertyKeyIterator() override = default;
PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;

View File

@ -55,6 +55,8 @@
#include <private/qjsvalue_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4module_p.h>
#include <private/qv4lookup_p.h>
#include <private/qv4identifiertable_p.h>
QT_BEGIN_NAMESPACE
@ -77,14 +79,11 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
if (!id.isString())
return Object::virtualGet(m, id, receiver, hasProperty);
return Object::virtualGet(resource, id, receiver, hasProperty);
const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@ -100,11 +99,11 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
}
return Object::virtualGet(m, id, receiver, hasProperty);
return Object::virtualGet(resource, id, receiver, hasProperty);
}
bool hasProp = false;
ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp));
ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@ -160,6 +159,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap
// sub-class as QML type and then instantiates it in .qml.
if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) {
// all bets are off, so don't try to optimize any lookups
lookup = nullptr;
if (performGobalLookUp())
return result->asReturnedValue();
}
@ -172,11 +173,35 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (hasProperty)
*hasProperty = true;
if (r.scriptIndex != -1) {
if (lookup) {
lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex;
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript;
return lookup->qmlContextPropertyGetter(lookup, v4, base);
}
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
if (scripts)
return scripts->get(r.scriptIndex);
return QV4::Encode::null();
} else if (r.type.isValid()) {
if (lookup) {
if (r.type.isSingleton()) {
QQmlEngine *e = v4->qmlEngine();
QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo();
siinfo->init(e);
if (siinfo->qobjectApi(e)) {
lookup->qmlContextSingletonLookup.singleton =
static_cast<Heap::Object*>(
Value::fromReturnedValue(
QQmlTypeWrapper::create(v4, nullptr, r.type)
).heapObject());
} else {
QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e)));
lookup->qmlContextSingletonLookup.singleton = o->d();
}
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton;
return lookup->qmlContextPropertyGetter(lookup, v4, base);
}
}
return QQmlTypeWrapper::create(v4, scopeObject, r.type);
} else if (r.importNamespace) {
return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace);
@ -188,6 +213,15 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine());
Lookup * const originalLookup = lookup;
decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty;
// minor optimization so we don't potentially try two property lookups on the same object
if (scopeObject == context->contextObject) {
scopeObject = nullptr;
contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty;
}
while (context) {
// Search context properties
@ -198,11 +232,17 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (propertyIdx != -1) {
if (propertyIdx < context->idValueCount) {
if (hasProperty)
*hasProperty = true;
if (lookup) {
lookup->qmlContextIdObjectLookup.objectId = propertyIdx;
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject;
return lookup->qmlContextPropertyGetter(lookup, v4, base);
}
if (ep->propertyCapture)
ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings);
if (hasProperty)
*hasProperty = true;
return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]);
} else {
@ -229,11 +269,30 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Search scope object
if (scopeObject) {
bool hasProp = false;
QQmlPropertyData *propertyData = nullptr;
QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject,
name, QV4::QObjectWrapper::CheckRevision, &hasProp));
name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData));
if (hasProp) {
if (hasProperty)
*hasProperty = true;
if (base)
*base = QV4::QObjectWrapper::wrap(v4, scopeObject);
if (lookup && propertyData) {
QQmlData *ddata = QQmlData::get(scopeObject, false);
if (ddata && ddata->propertyCache) {
ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject)));
const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
lookup->qobjectLookup.ic = That->internalClass();
lookup->qobjectLookup.staticQObject = nullptr;
lookup->qobjectLookup.propertyCache = ddata->propertyCache;
lookup->qobjectLookup.propertyCache->addref();
lookup->qobjectLookup.propertyData = propertyData;
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty;
}
}
return result->asReturnedValue();
}
}
@ -243,27 +302,73 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
// Search context object
if (context->contextObject) {
bool hasProp = false;
result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp);
QQmlPropertyData *propertyData = nullptr;
result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject,
name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData);
if (hasProp) {
if (hasProperty)
*hasProperty = true;
if (base)
*base = QV4::QObjectWrapper::wrap(v4, context->contextObject);
if (lookup && propertyData) {
QQmlData *ddata = QQmlData::get(context->contextObject, false);
if (ddata && ddata->propertyCache) {
ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject)));
const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue());
lookup->qobjectLookup.ic = That->internalClass();
lookup->qobjectLookup.staticQObject = nullptr;
lookup->qobjectLookup.propertyCache = ddata->propertyCache;
lookup->qobjectLookup.propertyCache->addref();
lookup->qobjectLookup.propertyData = propertyData;
lookup->qmlContextPropertyGetter = contextGetterFunction;
}
}
return result->asReturnedValue();
}
}
context = context->parent;
// As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond
// the immediate QML context (of the .qml file).
lookup = nullptr;
}
// Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
// true if we access properties of the global object.
if (performGobalLookUp())
return result->asReturnedValue();
if (originalLookup) {
// Try a lookup in the global object. It's theoretically possible to first find a property
// in the global object and then later a context property with the same name is added, but that
// never really worked as we used to detect access to global properties at type compile time anyway.
lookup = originalLookup;
result = lookup->resolveGlobalGetter(v4);
if (lookup->globalGetter != Lookup::globalGetterGeneric) {
if (hasProperty)
*hasProperty = true;
lookup->qmlContextGlobalLookup.getterTrampoline = lookup->globalGetter;
lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
return result->asReturnedValue();
}
lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
} else {
if (performGobalLookUp())
return result->asReturnedValue();
}
expressionContext->unresolvedNames = true;
return Encode::undefined();
}
ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m);
return getPropertyAndBase(This, id, receiver, hasProperty, /*base*/nullptr);
}
bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
@ -298,8 +403,16 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
while (context) {
const QV4::IdentifierHash &properties = context->propertyNames();
// Search context properties
if (properties.count() && properties.value(name) != -1)
return false;
if (properties.count()) {
const int propertyIndex = properties.value(name);
if (propertyIndex != -1) {
if (propertyIndex < context->idValueCount) {
v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue"));
return false;
}
return false;
}
}
// Search scope object
if (scopeObject &&
@ -323,6 +436,146 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
return false;
}
ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base)
{
Scope scope(engine);
PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
runtimeStrings[l->nameIndex]);
// Special hack for bounded signal expressions, where the parameters of signals are injected
// into the handler expression through the locals of the call context. So for onClicked: { ... }
// the parameters of the clicked signal are injected and we must allow for them to be found here
// before any other property from the QML context.
ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) {
uint index = ctx.d()->internalClass->indexOfValueOrGetter(name);
if (index < UINT_MAX)
return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue();
}
Scoped<QQmlContextWrapper> qmlContext(scope, engine->qmlContext()->qml());
bool hasProperty = false;
ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr,
&hasProperty, base, l));
if (!hasProperty)
return engine->throwReferenceError(name.toQString());
return result->asReturnedValue();
}
ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base)
Scope scope(engine);
Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
if (!qmlContext)
return QV4::Encode::null();
QQmlContextData *context = qmlContext->qmlContext();
if (!context)
return QV4::Encode::null();
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
if (!scripts)
return QV4::Encode::null();
return scripts->get(l->qmlContextScriptLookup.scriptIndex);
}
ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(engine)
Q_UNUSED(base)
return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue();
}
ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base)
Scope scope(engine);
Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
if (!qmlContext)
return QV4::Encode::null();
QQmlContextData *context = qmlContext->qmlContext();
if (!context)
return QV4::Encode::null();
QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine->qmlEngine());
const int objectId = l->qmlContextIdObjectLookup.objectId;
if (qmlEngine->propertyCapture)
qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings);
return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]);
}
ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base)
Scope scope(engine);
Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
if (!qmlContext)
return QV4::Encode::undefined();
QObject *scopeObject = qmlContext->qmlScope();
if (!scopeObject)
return QV4::Encode::undefined();
if (QQmlData::wasDeleted(scopeObject))
return QV4::Encode::undefined();
const auto revertLookup = [l, engine, base]() {
l->qobjectLookup.propertyCache->release();
l->qobjectLookup.propertyCache = nullptr;
l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
};
ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, scopeObject));
return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
}
ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base)
Scope scope(engine);
Scoped<QmlContext> qmlContext(scope, engine->qmlContext());
if (!qmlContext)
return QV4::Encode::undefined();
QQmlContextData *context = qmlContext->qmlContext();
if (!context)
return QV4::Encode::undefined();
QObject *contextObject = context->contextObject;
if (!contextObject)
return QV4::Encode::undefined();
if (QQmlData::wasDeleted(contextObject))
return QV4::Encode::undefined();
const auto revertLookup = [l, engine, base]() {
l->qobjectLookup.propertyCache->release();
l->qobjectLookup.propertyCache = nullptr;
l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base);
};
ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, contextObject));
return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup);
}
ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base)
{
Q_UNUSED(base);
ReturnedValue result = l->qmlContextGlobalLookup.getterTrampoline(l, engine);
// In the unlikely event of mutation of the global object, update the trampoline.
if (l->qmlContextPropertyGetter != lookupInGlobalObject) {
l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter;
l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject;
}
return result;
}
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);

View File

@ -99,8 +99,18 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
inline QObject *getScopeObject() const { return d()->scopeObject; }
inline QQmlContextData *getContext() const { return *d()->context; }
static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver,
bool *hasProperty, Value *base, Lookup *lookup = nullptr);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base);
static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext

View File

@ -56,6 +56,8 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
@ -231,7 +233,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject
return result;
}
ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired)
ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property)
{
QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
@ -257,7 +259,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
if (captureRequired && ep && ep->propertyCapture && !property->isConstant())
if (ep && ep->propertyCapture && !property->isConstant())
ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
if (property->isVarProperty()) {
@ -269,9 +271,53 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
}
}
static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr)
{
int index = 0;
if (name->equals(v4->id_destroy()))
index = QV4::QObjectMethod::DestroyMethod;
else if (name->equals(v4->id_toString()))
index = QV4::QObjectMethod::ToStringMethod;
else
return OptionalReturnedValue();
if (hasProperty)
*hasProperty = true;
ExecutionContext *global = v4->rootContext();
return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index));
}
static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj,
bool *hasProperty = nullptr)
{
if (!qmlContext || !qmlContext->imports)
return OptionalReturnedValue();
QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
if (hasProperty)
*hasProperty = true;
if (!r.isValid())
return OptionalReturnedValue();
if (r.scriptIndex != -1) {
return OptionalReturnedValue(QV4::Encode::undefined());
} else if (r.type.isValid()) {
return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums));
} else if (r.importNamespace) {
return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace,
Heap::QQmlTypeWrapper::ExcludeEnums));
}
Q_UNREACHABLE();
return OptionalReturnedValue();
}
ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode,
bool *hasProperty, bool includeImports) const
{
// Keep this code in sync with ::virtualResolveLookupGetter
if (QQmlData::wasDeleted(d()->object())) {
if (hasProperty)
*hasProperty = false;
@ -280,39 +326,17 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
ExecutionEngine *v4 = engine();
if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) {
int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
if (hasProperty)
*hasProperty = true;
ExecutionContext *global = v4->rootContext();
return QV4::QObjectMethod::create(global, d()->object(), index);
}
if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty))
return *methodValue;
QQmlPropertyData local;
QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local);
if (!result) {
// Check for attached properties
if (includeImports && name->startsWithUpper()) {
// Check for attached properties
if (qmlContext && qmlContext->imports) {
QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
if (hasProperty)
*hasProperty = true;
if (r.isValid()) {
if (r.scriptIndex != -1) {
return QV4::Encode::undefined();
} else if (r.type.isValid()) {
return QQmlTypeWrapper::create(v4, d()->object(),
r.type, Heap::QQmlTypeWrapper::ExcludeEnums);
} else if (r.importNamespace) {
return QQmlTypeWrapper::create(v4, d()->object(),
qmlContext->imports, r.importNamespace, Heap::QQmlTypeWrapper::ExcludeEnums);
}
Q_ASSERT(!"Unreachable");
}
}
if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty))
return *importProperty;
}
return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
}
@ -333,27 +357,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
return getProperty(v4, d()->object(), result);
}
ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired)
{
if (QQmlData::wasDeleted(object))
return QV4::Encode::null();
QQmlData *ddata = QQmlData::get(object, /*create*/false);
if (!ddata)
return QV4::Encode::undefined();
if (Q_UNLIKELY(!ddata->propertyCache)) {
ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object->metaObject());
ddata->propertyCache->addref();
}
QQmlPropertyCache *cache = ddata->propertyCache;
Q_ASSERT(cache);
QQmlPropertyData *property = cache->property(propertyIndex);
Q_ASSERT(property); // We resolved this property earlier, so it better exist!
return getProperty(engine, object, property, captureRequired);
}
ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty)
ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property)
{
if (QQmlData::wasDeleted(object)) {
if (hasProperty)
@ -361,13 +365,8 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
return QV4::Encode::null();
}
if (name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) {
int index = name->equals(engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
if (hasProperty)
*hasProperty = true;
ExecutionContext *global = engine->rootContext();
return QV4::QObjectMethod::create(global, object, index);
}
if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty))
return *methodValue;
QQmlData *ddata = QQmlData::get(object, false);
QQmlPropertyData local;
@ -385,6 +384,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC
if (hasProperty)
*hasProperty = true;
if (property)
*property = result;
return getProperty(engine, object, result);
} else {
// Check if this object is already wrapped.
@ -829,6 +831,71 @@ OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m,
return new QObjectWrapperOwnPropertyKeyIterator;
}
ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
{
// Keep this code in sync with ::getQmlProperty
PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
runtimeStrings[lookup->nameIndex]);
if (!id.isString())
return Object::virtualResolveLookupGetter(object, engine, lookup);
Scope scope(engine);
const QObjectWrapper *This = static_cast<const QObjectWrapper *>(object);
ScopedString name(scope, id.asStringOrSymbol());
QQmlContextData *qmlContext = engine->callingQmlContext();
QObject * const qobj = This->d()->object();
if (QQmlData::wasDeleted(qobj))
return QV4::Encode::undefined();
if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj))
return *methodValue;
QQmlData *ddata = QQmlData::get(qobj, false);
if (!ddata || !ddata->propertyCache) {
QQmlPropertyData local;
QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local);
return getProperty(engine, qobj, property);
}
QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
if (!property) {
// Check for attached properties
if (name->startsWithUpper()) {
if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj))
return *importProperty;
}
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
}
lookup->qobjectLookup.ic = This->internalClass();
lookup->qobjectLookup.staticQObject = nullptr;
lookup->qobjectLookup.propertyCache = ddata->propertyCache;
lookup->qobjectLookup.propertyCache->addref();
lookup->qobjectLookup.propertyData = property;
lookup->getter = QV4::QObjectWrapper::lookupGetter;
return lookup->getter(lookup, engine, *object);
}
ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
{
const auto revertLookup = [lookup, engine, &object]() {
lookup->qobjectLookup.propertyCache->release();
lookup->qobjectLookup.propertyCache = nullptr;
lookup->getter = Lookup::getterGeneric;
return Lookup::getterGeneric(lookup, engine, object);
};
return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup);
}
bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
const Value &value)
{
return Object::virtualResolveLookupSetter(object, engine, lookup, value);
}
namespace QV4 {
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
@ -1923,13 +1990,13 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
return method.asReturnedValue();
}
ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
method->d()->setPropertyCache(valueType->d()->propertyCache());
method->d()->setPropertyCache(valueType->propertyCache());
method->d()->index = index;
method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d());
method->d()->valueTypeWrapper.set(valueScope.engine, valueType);
return method.asReturnedValue();
}

View File

@ -60,6 +60,7 @@
#include <private/qv4value_p.h>
#include <private/qv4functionobject_p.h>
#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@ -165,7 +166,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const;
static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr);
static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr);
static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value);
@ -174,13 +175,18 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
using Object::get;
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired);
static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value);
void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value);
void destroyObject(bool lastCall);
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true);
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
template <typename ReversalFunctor> static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert);
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
protected:
static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value);
@ -216,6 +222,47 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje
return wrap_slowPath(engine, object);
}
template <typename ReversalFunctor>
inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup)
{
// 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->qobjectLookup.ic)
return revertLookup();
const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject :
static_cast<const Heap::QObjectWrapper *>(o);
QObject *qobj = This->object();
if (QQmlData::wasDeleted(qobj))
return QV4::Encode::undefined();
QQmlData *ddata = QQmlData::get(qobj, /*create*/false);
if (!ddata)
return revertLookup();
QQmlPropertyData *property = lookup->qobjectLookup.propertyData;
if (ddata->propertyCache != lookup->qobjectLookup.propertyCache) {
if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler()))
return revertLookup();
QQmlPropertyCache *fromMo = ddata->propertyCache;
QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache;
bool canConvert = false;
while (fromMo) {
if (fromMo == toMo) {
canConvert = true;
break;
}
fromMo = fromMo->parent();
}
if (!canConvert)
return revertLookup();
}
return getProperty(engine, qobj, property);
}
struct QQmlValueTypeWrapper;
struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
@ -226,7 +273,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 };
static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index);
static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index);
static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
int methodIndex() const { return d()->index; }
QObject *object() const { return d()->object(); }

View File

@ -1130,6 +1130,12 @@ ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function
return l->globalGetter(l, engine);
}
ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index)
{
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
return l->qmlContextPropertyGetter(l, engine, nullptr);
}
ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index)
{
Lookup *l = f->compilationUnit->runtimeLookups + index;
@ -1390,18 +1396,43 @@ uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const
return v->booleanValue();
}
static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
{
QString objectAsString = QStringLiteral("[null]");
if (!thisObject->isUndefined())
objectAsString = thisObject->toQStringNoThrow();
QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
.arg(propertyName, objectAsString);
return engine->throwTypeError(msg);
}
ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc)
{
Scope scope(engine);
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
Value function = Value::fromReturnedValue(l->globalGetter(l, engine));
if (!function.isFunctionObject())
return engine->throwTypeError();
Value thisObject = Value::undefinedValue();
if (!function.isFunctionObject())
return throwPropertyIsNotAFunctionTypeError(engine, &thisObject,
engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc);
}
ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index,
Value *argv, int argc)
{
Scope scope(engine);
ScopedValue thisObject(scope);
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject));
if (!function.isFunctionObject())
return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
return static_cast<FunctionObject &>(function).call(thisObject, argv, argc);
}
ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc)
{
Scope scope(engine);
@ -1412,13 +1443,8 @@ ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Val
if (engine->hasException)
return Encode::undefined();
if (!function) {
QString objectAsString = QStringLiteral("[null]");
if (!thisObject->isUndefined())
objectAsString = thisObject->toQStringNoThrow();
QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString);
return engine->throwTypeError(msg);
}
if (!function)
return throwPropertyIsNotAFunctionTypeError(engine, thisObject, QLatin1String("eval"));
if (function->d() == engine->evalFunction()->d())
return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true);
@ -1437,15 +1463,9 @@ ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Va
if (engine->hasException)
return Encode::undefined();
if (!f) {
QString objectAsString = QStringLiteral("[null]");
if (!thisObject->isUndefined())
objectAsString = thisObject->toQStringNoThrow();
QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
.arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
objectAsString);
return engine->throwTypeError(msg);
}
if (!f)
return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString());
return f->call(thisObject, argv, argc);
}
@ -1536,41 +1556,6 @@ ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Val
return static_cast<const FunctionObject &>(func).call(&thisObject, argv, argc);
}
ReturnedValue Runtime::CallQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &base,
int propertyIndex, Value argv[], int argc)
{
Scope scope(engine);
ScopedFunctionObject fo(scope, LoadQmlScopeObjectProperty::call(engine, base, propertyIndex,
/*captureRequired*/true));
if (!fo) {
QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex);
return engine->throwTypeError(error);
}
QObject *qmlScopeObj = static_cast<const QmlContext *>(&base)->d()->qml()->scopeObject;
ScopedValue qmlScopeValue(scope, QObjectWrapper::wrap(engine, qmlScopeObj));
return fo->call(qmlScopeValue, argv, argc);
}
ReturnedValue Runtime::CallQmlContextObjectProperty::call(ExecutionEngine *engine,
const Value &base,
int propertyIndex,
Value argv[],
int argc)
{
Scope scope(engine);
ScopedFunctionObject fo(scope, LoadQmlContextObjectProperty::call(engine, base, propertyIndex,
/*captureRequired*/true));
if (!fo) {
QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex);
return engine->throwTypeError(error);
}
QObject *qmlContextObj = static_cast<const QmlContext *>(&base)->d()->qml()->context->contextData()->contextObject;
ScopedValue qmlContextValue(scope, QObjectWrapper::wrap(engine, qmlContextObj));
return fo->call(qmlContextValue, argv, argc);
}
struct CallArgs {
Value *argv;
int argc;
@ -2013,66 +1998,12 @@ QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, i
return engine->newArrayObject(values, nValues)->asReturnedValue();
}
ReturnedValue Runtime::LoadQmlContext::call(ExecutionEngine *engine)
{
Heap::QmlContext *ctx = engine->qmlContext();
Q_ASSERT(ctx);
return ctx->asReturnedValue();
}
ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id)
{
Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>());
return ro->asReturnedValue();
}
ReturnedValue Runtime::LoadQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired);
}
ReturnedValue Runtime::LoadQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, Bool captureRequired)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired);
}
ReturnedValue Runtime::LoadQmlIdObject::call(ExecutionEngine *engine, const Value &c, uint index)
{
const QmlContext &qmlContext = static_cast<const QmlContext &>(c);
QQmlContextData *context = *qmlContext.d()->qml()->context;
if (!context || index >= (uint)context->idValueCount)
return Encode::undefined();
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr;
if (ep && ep->propertyCapture)
ep->propertyCapture->captureProperty(&context->idValues[index].bindings);
return QObjectWrapper::wrap(engine, context->idValues[index].data());
}
void Runtime::StoreQmlScopeObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value);
}
void Runtime::StoreQmlContextObjectProperty::call(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)
{
const QmlContext &c = static_cast<const QmlContext &>(context);
return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value);
}
ReturnedValue Runtime::LoadQmlImportedScripts::call(ExecutionEngine *engine)
{
QQmlContextData *context = engine->callingQmlContext();
if (!context)
return Encode::undefined();
return context->importedScripts.value();
}
ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj)
{
if (obj.isObject())

View File

@ -58,7 +58,6 @@ namespace QV4 {
typedef uint Bool;
struct Q_QML_PRIVATE_EXPORT Runtime {
typedef ReturnedValue (*UnaryOperation)(const Value &value);
typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right);
@ -87,6 +86,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
};
struct Q_QML_PRIVATE_EXPORT CallQmlContextPropertyLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, uint, Value[], int);
};
struct Q_QML_PRIVATE_EXPORT CallName : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, int, Value[], int);
@ -187,6 +190,10 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
{
static ReturnedValue call(ExecutionEngine *, Function *, int);
};
struct Q_QML_PRIVATE_EXPORT LoadQmlContextPropertyLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, uint);
};
struct Q_QML_PRIVATE_EXPORT GetLookup : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, Function *, const Value &, int);
@ -495,45 +502,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
static ReturnedValue call(Function *, int);
};
/* qml */
struct Q_QML_PRIVATE_EXPORT LoadQmlContext : Method<Throws::No>
{
static ReturnedValue call(ExecutionEngine *);
};
struct Q_QML_PRIVATE_EXPORT LoadQmlImportedScripts : Method<Throws::No>
{
static ReturnedValue call(ExecutionEngine *);
};
struct Q_QML_PRIVATE_EXPORT LoadQmlScopeObjectProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
};
struct Q_QML_PRIVATE_EXPORT LoadQmlContextObjectProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, int, Bool);
};
struct Q_QML_PRIVATE_EXPORT LoadQmlIdObject : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, uint);
};
struct Q_QML_PRIVATE_EXPORT CallQmlScopeObjectProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
};
struct Q_QML_PRIVATE_EXPORT CallQmlContextObjectProperty : Method<Throws::Yes>
{
static ReturnedValue call(ExecutionEngine *, const Value &, int, Value[], int);
};
struct Q_QML_PRIVATE_EXPORT StoreQmlScopeObjectProperty : Method<Throws::Yes>
{
static void call(ExecutionEngine *, const Value &, int, const Value &);
};
struct Q_QML_PRIVATE_EXPORT StoreQmlContextObjectProperty : Method<Throws::Yes>
{
static void call(ExecutionEngine *, const Value &, int, const Value &);
};
struct StackOffsets {
static const int tailCall_function = -1;
static const int tailCall_thisObject = -2;

View File

@ -71,6 +71,7 @@ public:
void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
private:
ExecutionEngine *engine;
};

View File

@ -130,7 +130,7 @@ PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Prope
return PropertyKey::fromArrayIndex(index);
} else if (arrayIndex == slen) {
if (s->arrayData()) {
arrayNode = s->sparseBegin();
SparseArrayNode *arrayNode = s->sparseBegin();
// iterate until we're past the end of the string
while (arrayNode && arrayNode->key() < slen)
arrayNode = arrayNode->nextNode();

View File

@ -877,6 +877,22 @@ struct ValueArray {
// have wrong offsets between host and target.
Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
class OptionalReturnedValue {
ReturnedValue value;
public:
OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {}
explicit OptionalReturnedValue(ReturnedValue v)
: value(v)
{
Q_ASSERT(!Value::fromReturnedValue(v).isEmpty());
}
ReturnedValue operator->() const { return value; }
ReturnedValue operator*() const { return value; }
explicit operator bool() const { return !Value::fromReturnedValue(value).isEmpty(); }
};
}
QT_END_NAMESPACE

View File

@ -669,6 +669,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadGlobalLookup)
MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
STORE_IP();
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
acc = l->qmlContextPropertyGetter(l, engine, nullptr);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(LoadQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(StoreNameStrict)
STORE_IP();
STORE_ACC();
@ -719,7 +727,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
STORE_ACC();
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
if (accumulator.isNullOrUndefined()) {
QString message = QStringLiteral("Cannot read property '%1' of %2")
.arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
.arg(accumulator.toQStringNoThrow());
acc = engine->throwTypeError(message);
goto handleUnwind;
}
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
@ -755,37 +773,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreSuperProperty)
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
STORE_ACC();
Runtime::StoreQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreScopeObjectProperty)
MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
STORE_IP();
acc = Runtime::LoadQmlScopeObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadScopeObjectProperty)
MOTH_BEGIN_INSTR(StoreContextObjectProperty)
STORE_IP();
STORE_ACC();
Runtime::StoreQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreContextObjectProperty)
MOTH_BEGIN_INSTR(LoadContextObjectProperty)
STORE_IP();
acc = Runtime::LoadQmlContextObjectProperty::call(engine, STACK_VALUE(base), propertyIndex, captureRequired);
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadContextObjectProperty)
MOTH_BEGIN_INSTR(LoadIdObject)
STORE_IP();
acc = Runtime::LoadQmlIdObject::call(engine, STACK_VALUE(base), index);
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadIdObject)
MOTH_BEGIN_INSTR(Yield)
frame->yield = code;
frame->yieldIsIterator = false;
@ -852,11 +839,23 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(CallPropertyLookup)
STORE_IP();
Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex;
if (stack[base].isNullOrUndefined()) {
QString message = QStringLiteral("Cannot call method '%1' of %2")
.arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
.arg(stack[base].toQStringNoThrow());
acc = engine->throwTypeError(message);
goto handleUnwind;
}
// ok to have the value on the stack here
Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base]));
if (Q_UNLIKELY(!f.isFunctionObject())) {
acc = engine->throwTypeError();
QString message = QStringLiteral("Property '%1' of object %2 is not a function")
.arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
.arg(stack[base].toQStringNoThrow());
acc = engine->throwTypeError(message);
goto handleUnwind;
}
@ -893,19 +892,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallGlobalLookup)
MOTH_BEGIN_INSTR(CallScopeObjectProperty)
MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
STORE_IP();
acc = Runtime::CallQmlScopeObjectProperty::call(engine, stack[base], name, stack + argv, argc);
acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallScopeObjectProperty)
MOTH_BEGIN_INSTR(CallContextObjectProperty)
STORE_IP();
acc = Runtime::CallQmlContextObjectProperty::call(engine, stack[base], name, stack + argv, argc);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
MOTH_END_INSTR(CallContextObjectProperty)
MOTH_END_INSTR(CallQmlContextPropertyLookup)
MOTH_BEGIN_INSTR(CallWithSpread)
STORE_IP();
@ -1520,14 +1512,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
#endif // QT_CONFIG(qml_debug)
MOTH_END_INSTR(Debug)
MOTH_BEGIN_INSTR(LoadQmlContext)
STACK_VALUE(result) = Runtime::LoadQmlContext::call(engine);
MOTH_END_INSTR(LoadQmlContext)
MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
STACK_VALUE(result) = Runtime::LoadQmlImportedScripts::call(engine);
MOTH_END_INSTR(LoadQmlImportedScripts)
handleUnwind:
Q_ASSERT(engine->hasException || frame->unwindLevel);
if (!frame->unwindHandler) {

View File

@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct Lookup;
struct OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
@ -84,6 +86,9 @@ struct VTable
typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget);
typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *);
typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &);
const VTable * const parent;
quint16 inlinePropertyOffset;
quint16 nInlineProperties;
@ -118,6 +123,9 @@ struct VTable
Call call;
CallAsConstructor callAsConstructor;
ResolveLookupGetter resolveLookupGetter;
ResolveLookupSetter resolveLookupSetter;
};
@ -142,6 +150,9 @@ protected:
static constexpr VTable::Call virtualCall = nullptr;
static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr;
static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr;
static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr;
};
#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
@ -181,6 +192,9 @@ protected:
\
classname::virtualCall, \
classname::virtualCallAsConstructor, \
\
classname::virtualResolveLookupGetter, \
classname::virtualResolveLookupSetter \
}
#define DEFINE_MANAGED_VTABLE(classname) \

View File

@ -614,16 +614,8 @@ bool Parser::parse(int startToken)
program = 0;
do {
if (++tos == stack_size) {
if (++tos == stack_size)
reallocateStack();
if (stack_size > 10000) {
// We're now in some serious right-recursive stuff, which will probably result in
// an AST that's so deep that recursively visiting it will run out of stack space.
const QString msg = QCoreApplication::translate("QQmlParser", "Maximum statement or expression depth exceeded");
diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
return false;
}
}
state_stack[tos] = action;

View File

@ -65,21 +65,6 @@ ClassExpression *asAnonymousClassDefinition(Node *n)
return c;
}
void Node::accept(Visitor *visitor)
{
if (visitor->preVisit(this)) {
accept0(visitor);
}
visitor->postVisit(this);
}
void Node::accept(Node *node, Visitor *visitor)
{
if (node)
node->accept(visitor);
}
ExpressionNode *Node::expressionCast()
{
return nullptr;

View File

@ -271,11 +271,29 @@ public:
virtual FunctionExpression *asFunctionDefinition();
virtual ClassExpression *asClassDefinition();
void accept(Visitor *visitor);
static void accept(Node *node, Visitor *visitor);
inline void accept(Visitor *visitor)
{
Visitor::RecursionDepthCheck recursionCheck(visitor);
if (recursionCheck()) {
if (visitor->preVisit(this))
accept0(visitor);
visitor->postVisit(this);
} else {
visitor->throwRecursionDepthError();
}
}
inline static void accept(Node *node, Visitor *visitor)
{
if (node)
node->accept(visitor);
}
// ### Remove when we can. This is part of the qmldevtools library, though.
inline static void acceptChild(Node *node, Visitor *visitor)
{ return accept(node, visitor); } // ### remove
{
return accept(node, visitor);
}
virtual void accept0(Visitor *visitor) = 0;
virtual SourceLocation firstSourceLocation() const = 0;

View File

@ -43,7 +43,7 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
Visitor::Visitor()
Visitor::Visitor(quint16 parentRecursionDepth) : m_recursionDepth(parentRecursionDepth)
{
}

View File

@ -61,7 +61,33 @@ namespace QQmlJS { namespace AST {
class QML_PARSER_EXPORT Visitor
{
public:
Visitor();
class RecursionDepthCheck
{
Q_DISABLE_COPY(RecursionDepthCheck)
public:
RecursionDepthCheck(RecursionDepthCheck &&) = delete;
RecursionDepthCheck &operator=(RecursionDepthCheck &&) = delete;
RecursionDepthCheck(Visitor *visitor) : m_visitor(visitor)
{
++(m_visitor->m_recursionDepth);
}
~RecursionDepthCheck()
{
--(m_visitor->m_recursionDepth);
}
bool operator()() const {
return m_visitor->m_recursionDepth < s_recursionLimit;
}
private:
static const quint16 s_recursionLimit = 4096;
Visitor *m_visitor;
};
Visitor(quint16 parentRecursionDepth = 0);
virtual ~Visitor();
virtual bool preVisit(Node *) { return true; }
@ -374,6 +400,14 @@ public:
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
virtual void throwRecursionDepthError() = 0;
quint16 recursionDepth() const { return m_recursionDepth; }
protected:
quint16 m_recursionDepth = 0;
friend class RecursionDepthCheck;
};
} } // namespace AST

View File

@ -584,7 +584,6 @@ namespace QtQml {
const QMetaObject *, bool create);
#ifndef Q_QDOC
}
#endif
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wheader-hygiene")
@ -594,6 +593,8 @@ using namespace QtQml;
QT_WARNING_POP
#endif // Q_QDOC
//The C++ version of protected namespaces in qmldir
Q_QML_EXPORT bool qmlProtectModule(const char* uri, int majVersion);
Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor);

View File

@ -260,8 +260,6 @@ protected:
} else {
clearError();
}
cancelPermanentGuards();
}
ep->dereferenceScarceResources();
@ -643,24 +641,22 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
if (!m_target.data())
return dependencies;
for (const auto &guardList : { permanentGuards, activeGuards }) {
for (QQmlJavaScriptExpressionGuard *guard = guardList.first(); guard; guard = guardList.next(guard)) {
if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
continue;
for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(guard)) {
if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*.
continue;
QObject *senderObject = guard->senderAsObject();
if (!senderObject)
continue;
QObject *senderObject = guard->senderAsObject();
if (!senderObject)
continue;
const QMetaObject *senderMeta = senderObject->metaObject();
if (!senderMeta)
continue;
const QMetaObject *senderMeta = senderObject->metaObject();
if (!senderMeta)
continue;
for (int i = 0; i < senderMeta->propertyCount(); i++) {
QMetaProperty property = senderMeta->property(i);
if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
}
for (int i = 0; i < senderMeta->propertyCount(); i++) {
QMetaProperty property = senderMeta->property(i);
if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) {
dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name())));
}
}
}
@ -670,7 +666,7 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
bool QQmlBinding::hasDependencies() const
{
return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured();
return !activeGuards.isEmpty() || translationsCaptured();
}
class QObjectPointerBinding: public QQmlNonbindingBinding

View File

@ -1471,7 +1471,7 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix
QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl);
Q_ASSERT(!localFileOrQrc.isEmpty());
QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri));
const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1);
if (!typeLoader->directoryExists(dir)) {
if (!isImplicitImport) {
QQmlError error;

View File

@ -109,7 +109,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
}
clearActiveGuards();
clearPermanentGuards();
clearError();
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
m_scopeObject.asT2()->_s = nullptr;
@ -118,12 +117,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
{
activeGuards.setFlagValue(v);
permanentGuards.setFlagValue(v);
if (!v) {
if (!v)
clearActiveGuards();
clearPermanentGuards();
m_permanentDependenciesRegistered = false;
}
}
void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
@ -216,10 +211,6 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
QV4::Scope scope(v4);
QV4::ScopedValue result(scope, res);
if (v4Function->hasQmlDependencies) {
QV4::Heap::QmlContext *qc = m_qmlScope.as<QV4::QmlContext>()->d();
QQmlPropertyCapture::registerQmlDependencies(qc, v4, v4Function->compiledFunction);
}
if (scope.hasException()) {
if (watcher.wasDeleted())
@ -254,7 +245,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
return result->asReturnedValue();
}
void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
void QQmlPropertyCapture::captureProperty(QQmlNotifier *n)
{
if (watcher->wasDeleted())
return;
@ -274,17 +265,14 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration)
g->connect(n);
}
if (duration == Permanently)
expression->permanentGuards.prepend(g);
else
expression->activeGuards.prepend(g);
expression->activeGuards.prepend(g);
}
/*! \internal
\a n is in the signal index range (see QObjectPrivate::signalIndex()).
*/
void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration, bool doNotify)
void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotify)
{
if (watcher->wasDeleted())
return;
@ -323,63 +311,10 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur
g->connect(o, n, engine, doNotify);
}
if (duration == Permanently)
expression->permanentGuards.prepend(g);
else
expression->activeGuards.prepend(g);
expression->activeGuards.prepend(g);
}
}
void QQmlPropertyCapture::registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction)
{
// Let the caller check and avoid the function call :)
Q_ASSERT(compiledFunction->hasQmlDependencies());
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine());
if (!ep)
return;
QQmlPropertyCapture *capture = ep->propertyCapture;
if (!capture || capture->watcher->wasDeleted())
return;
if (capture->expression->m_permanentDependenciesRegistered)
return;
capture->expression->m_permanentDependenciesRegistered = true;
QV4::Heap::QQmlContextWrapper *wrapper = context->qml();
QQmlContextData *qmlContext = wrapper->context->contextData();
const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable();
const int idObjectDependencyCount = compiledFunction->nDependingIdObjects;
for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) {
Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount);
capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings,
QQmlPropertyCapture::Permanently);
}
Q_ASSERT(qmlContext->contextObject);
const quint32_le *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable();
const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties;
for (int i = 0; i < contextPropertyDependencyCount; ++i) {
const int propertyIndex = *contextPropertyDependency++;
const int notifyIndex = *contextPropertyDependency++;
capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex,
QQmlPropertyCapture::Permanently);
}
QObject *scopeObject = wrapper->scopeObject;
const quint32_le *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable();
const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties;
for (int i = 0; i < scopePropertyDependencyCount; ++i) {
const int propertyIndex = *scopePropertyDependency++;
const int notifyIndex = *scopePropertyDependency++;
capture->captureProperty(scopeObject, propertyIndex, notifyIndex,
QQmlPropertyCapture::Permanently);
}
}
QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
{
Q_UNUSED(engine);
@ -471,13 +406,6 @@ void QQmlJavaScriptExpression::clearActiveGuards()
g->Delete();
}
void QQmlJavaScriptExpression::clearPermanentGuards()
{
m_permanentDependenciesRegistered = false;
while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst())
g->Delete();
}
void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
{
QQmlJavaScriptExpression *expression =

View File

@ -144,7 +144,6 @@ public:
QQmlError error(QQmlEngine *) const;
void clearError();
void clearActiveGuards();
void clearPermanentGuards();
QQmlDelayedError *delayedError();
static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope,
@ -153,14 +152,6 @@ public:
protected:
void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line);
void cancelPermanentGuards() const
{
if (m_permanentDependenciesRegistered) {
for (QQmlJavaScriptExpressionGuard *it = permanentGuards.first(); it; it = permanentGuards.next(it))
it->cancelNotify();
}
}
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f);
void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
@ -169,7 +160,6 @@ protected:
// activeGuards:flag2 - useSharedContext
QBiPointer<QObject, DeleteWatcher> m_scopeObject;
QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards;
QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards;
void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); }
bool translationsCaptured() const { return m_error.flag(); }
@ -186,7 +176,6 @@ private:
QQmlContextData *m_context;
QQmlJavaScriptExpression **m_prevExpression;
QQmlJavaScriptExpression *m_nextExpression;
bool m_permanentDependenciesRegistered = false;
QV4::PersistentValue m_qmlScope;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
@ -204,14 +193,8 @@ public:
Q_ASSERT(errorString == nullptr);
}
enum Duration {
OnlyOnce,
Permanently
};
static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction);
void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce);
void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true);
void captureProperty(QQmlNotifier *);
void captureProperty(QObject *, int, int, bool doNotify = true);
void captureTranslation() { translationCaptured = true; }
QQmlEngine *engine;

View File

@ -48,6 +48,8 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
QT_BEGIN_NAMESPACE
@ -170,6 +172,7 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n
ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
// Keep this code in sync with ::virtualResolveLookupGetter
Q_ASSERT(m->as<QQmlTypeWrapper>());
if (!id.isString())
@ -426,6 +429,64 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const
return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType));
}
ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
{
// Keep this code in sync with ::virtualGet
PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
if (!id.isString())
return Object::virtualResolveLookupGetter(object, engine, lookup);
Scope scope(engine);
const QQmlTypeWrapper *This = static_cast<const QQmlTypeWrapper *>(object);
ScopedString name(scope, id.asStringOrSymbol());
QQmlContextData *qmlContext = engine->callingQmlContext();
Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(This));
QQmlType type = w->d()->type();
if (type.isValid()) {
if (type.isSingleton()) {
QQmlEngine *e = engine->qmlEngine();
QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo();
siinfo->init(e);
QObject *qobjectSingleton = siinfo->qobjectApi(e);
if (qobjectSingleton) {
const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums;
if (!includeEnums || !name->startsWithUpper()) {
QQmlData *ddata = QQmlData::get(qobjectSingleton, false);
if (ddata && ddata->propertyCache) {
ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton)));
QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext);
if (property) {
lookup->qobjectLookup.ic = This->internalClass();
lookup->qobjectLookup.staticQObject = static_cast<Heap::QObjectWrapper *>(val->heapObject());
lookup->qobjectLookup.propertyCache = ddata->propertyCache;
lookup->qobjectLookup.propertyCache->addref();
lookup->qobjectLookup.propertyData = property;
lookup->getter = QV4::QObjectWrapper::lookupGetter;
return lookup->getter(lookup, engine, *This);
}
// Fall through to base implementation
}
// Fall through to base implementation
}
// Fall through to base implementation
}
// Fall through to base implementation
}
// Fall through to base implementation
}
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
}
bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
{
return Object::virtualResolveLookupSetter(object, engine, lookup, value);
}
void Heap::QQmlScopedEnumWrapper::destroy()
{
QQmlType::derefHandle(typePrivate);

View File

@ -111,6 +111,9 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *,
Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);

View File

@ -51,6 +51,8 @@
#include <private/qv4stackframe_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4identifiertable_p.h>
#include <private/qv4lookup_p.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
@ -372,6 +374,117 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
return Encode(b->engine()->newString(result));
}
Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine,
Heap::QQmlValueTypeWrapper *valueTypeWrapper,
QQmlPropertyData *property)
{
if (property->isFunction()) {
// calling a Q_INVOKABLE function of a value type
return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex());
}
#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
if (property->propType() == metatype) { \
cpptype v; \
void *args[] = { &v, nullptr }; \
metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), \
QMetaObject::ReadProperty, index, args); \
return QV4::Encode(constructor(v)); \
}
const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject();
int index = property->coreIndex();
QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
// These four types are the most common used by the value type wrappers
VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int);
VALUE_TYPE_LOAD(QMetaType::Int, int, int);
VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString);
VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
QVariant v;
void *args[] = { nullptr, nullptr };
if (property->propType() == QMetaType::QVariant) {
args[0] = &v;
} else {
v = QVariant(property->propType(), static_cast<void *>(nullptr));
args[0] = v.data();
}
metaObject->d.static_metacall(reinterpret_cast<QObject*>(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty,
index, args);
return engine->fromVariant(v);
#undef VALUE_TYPE_LOAD
}
ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine,
Lookup *lookup)
{
PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
runtimeStrings[lookup->nameIndex]);
if (!id.isString())
return Object::virtualResolveLookupGetter(object, engine, lookup);
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(object);
QV4::ExecutionEngine *v4 = r->engine();
Scope scope(v4);
ScopedString name(scope, id.asStringOrSymbol());
// Note: readReferenceValue() can change the reference->type.
if (const QQmlValueTypeReference *reference = r->as<QQmlValueTypeReference>()) {
if (!reference->readReferenceValue())
return Value::undefinedValue().asReturnedValue();
}
QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
if (!result)
return QV4::Object::virtualResolveLookupGetter(object, engine, lookup);
lookup->qgadgetLookup.ic = r->internalClass();
lookup->qgadgetLookup.propertyCache = r->d()->propertyCache();
lookup->qgadgetLookup.propertyCache->addref();
lookup->qgadgetLookup.propertyData = result;
lookup->getter = QQmlValueTypeWrapper::lookupGetter;
return lookup->getter(lookup, engine, *object);
}
ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object)
{
const auto revertLookup = [lookup, engine, &object]() {
lookup->qgadgetLookup.propertyCache->release();
lookup->qgadgetLookup.propertyCache = nullptr;
lookup->getter = Lookup::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->propertyCache() != lookup->qgadgetLookup.propertyCache)
return revertLookup();
if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) {
Scope scope(engine);
Scoped<QQmlValueTypeReference> referenceWrapper(scope, valueTypeWrapper);
referenceWrapper->readReferenceValue();
}
QQmlPropertyData *property = lookup->qgadgetLookup.propertyData;
return getGadgetProperty(engine, valueTypeWrapper, property);
}
bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup,
const Value &value)
{
return Object::virtualResolveLookupSetter(object, engine, lookup, value);
}
ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
@ -397,43 +510,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id,
if (hasProperty)
*hasProperty = true;
if (result->isFunction())
// calling a Q_INVOKABLE function of a value type
return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex());
#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \
if (result->propType() == metatype) { \
cpptype v; \
void *args[] = { &v, 0 }; \
metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args); \
return QV4::Encode(constructor(v)); \
}
const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
int index = result->coreIndex();
QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index);
void *gadget = r->d()->gadgetPtr;
// These four types are the most common used by the value type wrappers
VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal);
VALUE_TYPE_LOAD(QMetaType::Int || result->isEnum(), int, int);
VALUE_TYPE_LOAD(QMetaType::Int, int, int);
VALUE_TYPE_LOAD(QMetaType::QString, QString, v4->newString);
VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool);
QVariant v;
void *args[] = { nullptr, nullptr };
if (result->propType() == QMetaType::QVariant) {
args[0] = &v;
} else {
v = QVariant(result->propType(), static_cast<void *>(nullptr));
args[0] = v.data();
}
metaObject->d.static_metacall(reinterpret_cast<QObject*>(gadget), QMetaObject::ReadProperty, index, args);
return v4->fromVariant(v);
#undef VALUE_TYPE_ACCESSOR
return getGadgetProperty(v4, r->d(), result);
}
bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)

View File

@ -112,6 +112,9 @@ public:
static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
static ReturnedValue lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object);
static void initProto(ExecutionEngine *v4);
};

View File

@ -52,6 +52,7 @@
#include <private/qv4dateobject_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4alloca_p.h>
#include <private/qv4lookup_p.h>
#include <qqmlcontext.h>
#include <qqmlinfo.h>
@ -1604,8 +1605,7 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
if (ep && ep->propertyCapture)
ep->propertyCapture->captureProperty(that->object(), -1, role->index,
QQmlPropertyCapture::OnlyOnce, false);
ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false);
}
const int elementIndex = that->d()->elementIndex();
@ -1613,6 +1613,12 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va
return that->engine()->fromVariant(value);
}
ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
{
lookup->getter = Lookup::getterFallback;
return lookup->getter(lookup, engine, *object);
}
struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
int roleNameIndex = 0;

View File

@ -181,6 +181,8 @@ struct ModelObject : public QObjectWrapper
protected:
static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
};

View File

@ -525,7 +525,7 @@ public:
metaObject.reset(builder.toMetaObject());
*static_cast<QMetaObject *>(this) = *metaObject;
propertyCache = new QQmlPropertyCache(metaObject.data(), model.modelItemRevision);
propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision));
}
};
@ -659,8 +659,8 @@ public:
{
VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this);
if (!propertyCache) {
dataType->propertyCache = new QQmlPropertyCache(
&QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision);
dataType->propertyCache.adopt(new QQmlPropertyCache(
&QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision));
}
return new QQmlDMListAccessorData(

View File

@ -120,7 +120,7 @@ interpolation behaviors are definable. The
{Animation and Transitions} article has more information about creating state
animations.
The \l {animation/states}{States and Transitions example}
The \l {Qt Quick Examples - Animation}{Animation} example
demonstrates how to declare a basic set of states and apply animated
transitions between them.

View File

@ -39,6 +39,8 @@
#include "qquickdragaxis_p.h"
#include <limits>
QT_BEGIN_NAMESPACE
QQuickDragAxis::QQuickDragAxis()
: m_minimum(-std::numeric_limits<qreal>::max())
, m_maximum(std::numeric_limits<qreal>::max())
@ -73,3 +75,4 @@ void QQuickDragAxis::setEnabled(bool enabled)
emit enabledChanged();
}
QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@ -51,10 +51,11 @@
// We mean it.
//
#include <QtCore/qobject.h>
#include <QtCore/qglobal.h>
#include <private/qtquickglobal_p.h>
class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject
QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QQuickDragAxis : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
@ -84,4 +85,6 @@ private:
bool m_enabled;
};
QT_END_NAMESPACE
#endif // QQUICKDRAGAXIS_P_H

View File

@ -56,7 +56,7 @@
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
class Q_QUICK_PRIVATE_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
{
Q_OBJECT
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)

View File

@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
class Q_QUICK_PRIVATE_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged)

View File

@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
class Q_QUICK_PRIVATE_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged)

View File

@ -59,7 +59,7 @@
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
class Q_QUICK_PRIVATE_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
{
Q_OBJECT
Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged)

View File

@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE
class QQuickPointerDeviceHandlerPrivate;
class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
{
Q_OBJECT
Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged)

View File

@ -56,7 +56,7 @@
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
class Q_QUICK_PRIVATE_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
{
Q_DECLARE_PUBLIC(QQuickPointerDeviceHandler)

View File

@ -55,7 +55,7 @@
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QQuickPointHandler : public QQuickSinglePointHandler
class Q_QUICK_PRIVATE_EXPORT QQuickPointHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)

View File

@ -58,7 +58,7 @@
QT_BEGIN_NAMESPACE
class Q_AUTOTEST_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
class Q_QUICK_PRIVATE_EXPORT QQuickTapHandler : public QQuickSinglePointHandler
{
Q_OBJECT
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged)

View File

@ -43,6 +43,8 @@
#include <QtGui/qvector2d.h>
#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE
QQuickDefaultClipNode::QQuickDefaultClipNode(const QRectF &rect)
: m_rect(rect)
, m_radius(0)
@ -117,3 +119,4 @@ void QQuickDefaultClipNode::updateGeometry()
markDirty(DirtyGeometry);
}
QT_END_NAMESPACE

View File

@ -54,6 +54,8 @@
#include <private/qtquickglobal_p.h>
#include <QtQuick/qsgnode.h>
QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QQuickDefaultClipNode : public QSGClipNode
{
public:
@ -78,4 +80,6 @@ private:
QSGGeometry m_geometry;
};
QT_END_NAMESPACE
#endif // QQUICKCLIPNODE_P_H

View File

@ -96,7 +96,7 @@ public:
// Uppermost 8 bits are reserved for internal use.
IsVisitableNode = 0x01000000
#ifdef Q_CLANG_QDOC
InternalReserved = 0x01000000
, InternalReserved = 0x01000000
#endif
};
Q_DECLARE_FLAGS(Flags, Flag)

View File

@ -338,7 +338,7 @@ void tst_QQmlPreview::fps()
QVERIFY(m_client);
m_client->triggerLoad(testFileUrl(file));
if (QGuiApplication::platformName() != "offscreen") {
QTRY_VERIFY(m_frameStats.numSyncs > 10);
QTRY_VERIFY_WITH_TIMEOUT(m_frameStats.numSyncs > 10, 10000);
QVERIFY(m_frameStats.minSync <= m_frameStats.maxSync);
QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs >= m_frameStats.minSync - 1);
QVERIFY(m_frameStats.totalSync / m_frameStats.numSyncs <= m_frameStats.maxSync);

View File

@ -8,6 +8,8 @@ QtObject {
signal testSignal
onTestSignal: count++
readonly property string scopeObjectAsString: this.toString()
property int funcCount: 0
function testFunction() {
funcCount++;

View File

@ -364,6 +364,8 @@ private slots:
void arrayAndException();
void numberToStringWithRadix();
void tailCallWithArguments();
void deleteSparseInIteration();
void saveAccumulatorBeforeToInt32();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@ -1607,7 +1609,7 @@ void tst_qqmlecmascript::aliasPropertyReset()
// test that a manual write (of undefined) to a non-resettable property fails properly
QUrl url = testFileUrl("aliasreset/aliasPropertyReset.error.1.qml");
QString warning1 = url.toString() + QLatin1String(": Error: Cannot assign [undefined] to int");
QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
QQmlComponent e1(&engine, url);
object = e1.create();
QVERIFY(object != nullptr);
@ -6492,7 +6494,8 @@ void tst_qqmlecmascript::signalHandlers()
QMetaObject::invokeMethod(o.data(), "testSignalHandlerCall");
QCOMPARE(o->property("count").toInt(), 1);
QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
QString scopeObjectAsString = o->property("scopeObjectAsString").toString();
QCOMPARE(o->property("errorString").toString(), QString("TypeError: Property 'onTestSignal' of object %1 is not a function").arg(scopeObjectAsString));
QCOMPARE(o->property("funcCount").toInt(), 0);
QMetaObject::invokeMethod(o.data(), "testSignalConnection");
@ -8187,12 +8190,11 @@ void tst_qqmlecmascript::stackLimits()
void tst_qqmlecmascript::idsAsLValues()
{
QQmlEngine engine;
QString err = QString(QLatin1String("%1:5 left-hand side of assignment operator is not an lvalue\n")).arg(testFileUrl("idAsLValue.qml").toString());
QString err = QString(QLatin1String("%1:5: Error: left-hand side of assignment operator is not an lvalue")).arg(testFileUrl("idAsLValue.qml").toString());
QQmlComponent component(&engine, testFileUrl("idAsLValue.qml"));
QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
QTest::ignoreMessage(QtWarningMsg, qPrintable(err));
MyQmlObject *object = qobject_cast<MyQmlObject*>(component.create());
QVERIFY(!object);
QCOMPARE(component.errorString(), err);
}
void tst_qqmlecmascript::qtbug_34792()
@ -8933,6 +8935,38 @@ void tst_qqmlecmascript::tailCallWithArguments()
QCOMPARE(value.toInt(), 1);
}
void tst_qqmlecmascript::deleteSparseInIteration()
{
QJSEngine engine;
const QJSValue value = engine.evaluate(
"(function() {\n"
" var obj = { 1: null, 2: null, 4096: null };\n"
" var iterated = [];\n"
" for (var t in obj) {\n"
" if (t == 2)\n"
" delete obj[t];\n"
" iterated.push(t);\n"
" }\n"
" return iterated;"
"})()");
QVERIFY(value.isArray());
QCOMPARE(value.property("length").toInt(), 3);
QCOMPARE(value.property("0").toInt(), 1);
QCOMPARE(value.property("1").toInt(), 2);
QCOMPARE(value.property("2").toInt(), 4096);
}
void tst_qqmlecmascript::saveAccumulatorBeforeToInt32()
{
QJSEngine engine;
// Infinite recursion produces a range error, but should not crash.
// Also, any GC runs in between should not trash the temporary results of "a+a".
const QJSValue value = engine.evaluate("function a(){a(a&a+a)}a()");
QVERIFY(value.isError());
QCOMPARE(value.toString(), QLatin1String("RangeError: Maximum call stack size exceeded."));
}
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"

View File

@ -0,0 +1,7 @@
import QtQml 2.2
import "$(INTERCEPT)" as Intercepted
QtObject {
property QtObject view: Intercepted.View {}
}

View File

@ -0,0 +1,5 @@
import QtQml 2.2
QtObject {
property int foo: 12
}

View File

@ -0,0 +1,2 @@
module SomeModule
View 1.0 View.qml

View File

@ -28,6 +28,7 @@
#include <QtTest/QtTest>
#include <QQmlApplicationEngine>
#include <QQmlAbstractUrlInterceptor>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <private/qqmlimport_p.h>
@ -43,6 +44,7 @@ private slots:
void uiFormatLoading();
void completeQmldirPaths_data();
void completeQmldirPaths();
void interceptQmldir();
void cleanup();
};
@ -185,6 +187,32 @@ void tst_QQmlImport::completeQmldirPaths()
QCOMPARE(QQmlImports::completeQmldirPaths(uri, basePaths, majorVersion, minorVersion), expectedPaths);
}
class QmldirUrlInterceptor : public QQmlAbstractUrlInterceptor {
public:
QUrl intercept(const QUrl &url, DataType type) override
{
if (type != UrlString && !url.isEmpty() && url.isValid()) {
QString str = url.toString(QUrl::None);
return str.replace(QStringLiteral("$(INTERCEPT)"), QStringLiteral("intercepted"));
}
return url;
}
};
void tst_QQmlImport::interceptQmldir()
{
QQmlEngine engine;
QmldirUrlInterceptor interceptor;
engine.setUrlInterceptor(&interceptor);
QQmlComponent component(&engine);
component.loadUrl(testFileUrl("interceptQmldir.qml"));
QVERIFY(component.isReady());
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
}
QTEST_MAIN(tst_QQmlImport)
#include "tst_qqmlimport.moc"

View File

@ -0,0 +1,20 @@
import QtQuick 2.0
Item {
property int edgeWidth: bg.width
Rectangle {
id: bg
}
states: [
State {
when: 1 === 1
PropertyChanges {
target: bg
anchors.left: parent.left
anchors.right: parent.right
}
}
]
}

View File

@ -0,0 +1,10 @@
import QtQuick 2.9
Item {
width: 200
property int edgeWidth: edge.edgeWidth
EdgeObject {
id: edge
anchors.fill: parent
}
}

View File

@ -6,5 +6,18 @@ QtObject {
y = this.x;
}
property var f: g
Component.onCompleted: f()
property int a: 42
property int b: 0
function g_subobj(){
b = this.a;
}
property var f_subobj: g_subobj
property QtObject subObject: QtObject {
property int a: 100
Component.onCompleted: f_subobj()
}
}

View File

@ -299,6 +299,7 @@ private slots:
void retrieveQmlTypeId();
void polymorphicFunctionLookup();
void anchorsToParentInPropertyChanges();
private:
QQmlEngine engine;
@ -5022,6 +5023,8 @@ void tst_qqmllanguage::thisInQmlScope()
QVERIFY(!o.isNull());
QCOMPARE(o->property("x"), QVariant(42));
QCOMPARE(o->property("y"), QVariant(42));
QCOMPARE(o->property("a"), QVariant(42));
QCOMPARE(o->property("b"), QVariant(42));
}
void tst_qqmllanguage::valueTypeGroupPropertiesInBehavior()
@ -5069,6 +5072,16 @@ void tst_qqmllanguage::polymorphicFunctionLookup()
QVERIFY(o->property("ok").toBool());
}
void tst_qqmllanguage::anchorsToParentInPropertyChanges()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("anchorsToParentInPropertyChagnes.qml"));
VERIFY_ERRORS(0);
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
QTRY_COMPARE(o->property("edgeWidth").toInt(), 200);
}
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"

View File

@ -349,7 +349,7 @@ void tst_qqmlnotifier::deleteFromHandler()
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("objectRenamer.qml"));
QPointer<QObject> mess = component.create();
QObject::connect(mess, &QObject::objectNameChanged, [&]() { delete mess; });
QObject::connect(mess.data(), &QObject::objectNameChanged, [&]() { delete mess; });
QTRY_VERIFY(mess.isNull()); // BANG!
} else {
QProcess process;

View File

@ -105,6 +105,11 @@ public:
{
nodeStack.removeLast();
}
void throwRecursionDepthError() final
{
QFAIL("Maximum statement or expression depth exceeded");
}
};
}

View File

@ -33,6 +33,7 @@
#include <private/qv4mm_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qjsvalue_p.h>
#include "../../shared/util.h"
@ -46,6 +47,7 @@ private slots:
void gcStats();
void multiWrappedQObjects();
void accessParentOnDestruction();
void clearICParent();
};
void tst_qv4mm::gcStats()
@ -108,6 +110,30 @@ void tst_qv4mm::accessParentOnDestruction()
QCOMPARE(obj->property("destructions").toInt(), 100);
}
void tst_qv4mm::clearICParent()
{
QJSEngine engine;
QJSValue value = engine.evaluate(
"(function() {\n"
" var test = Object.create(null);\n"
" for (var i = 0; i < 100; i++)\n"
" test[(\"key_\"+i)] = true;\n"
" for (var i = 0; i < 100; i++)\n"
" delete test[\"key_\" + i];\n"
" return test;\n"
"})();"
);
engine.collectGarbage();
QV4::Value *v4Value = QJSValuePrivate::getValue(&value);
QVERIFY(v4Value);
QV4::Heap::Object *v4Object = v4Value->toObject(engine.handle());
QVERIFY(v4Object);
// It should garbage collect the parents of the internalClass,
// as those aren't used anywhere else.
QCOMPARE(v4Object->internalClass->parent, nullptr);
}
QTEST_MAIN(tst_qv4mm)
#include "tst_qv4mm.moc"

View File

@ -0,0 +1,2 @@
[AnimatedImage::test_crashRaceCondition_replyFinished]
osx-10.13

View File

@ -6637,7 +6637,7 @@ void tst_QQuickGridView::contentHeightWithDelayRemove()
QCOMPARE(qRound(gridview->contentHeight()), qRound(initialContentHeight));
QTRY_COMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
} else {
QCOMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
QTRY_COMPARE(qRound(gridview->contentHeight()), eventualContentHeight);
}
delete window;

View File

@ -1,5 +1,7 @@
[enforceRange_withoutHighlight]
osx
opensuse-42.3
opensuse-leap
#QTBUG-53863
[populateTransitions]
opensuse-42.1

View File

@ -134,14 +134,12 @@ void tst_qquickrectangle::gradient_separate()
// Start off clean
QQuickItemPrivate *rectPriv = QQuickItemPrivate::get(rect);
bool isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content;
QVERIFY(!isDirty);
QTRY_COMPARE(rectPriv->dirtyAttributes & QQuickItemPrivate::Content, 0);
QMetaObject::invokeMethod(rect, "changeGradient");
// Changing the gradient should have scheduled an update of the item.
isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content;
QVERIFY(isDirty);
QVERIFY((rectPriv->dirtyAttributes & QQuickItemPrivate::Content) != 0);
}
// When a gradient is changed, every Rectangle connected to it must update.

View File

@ -1,2 +1,6 @@
[buttonOnDelayedPressFlickable]
windows gcc developer-build
# QTBUG-74517
[buttonOnFlickable]
windows gcc developer-build

View File

@ -55,17 +55,8 @@ QSet<QString> illegalNames;
void setupIllegalNames()
{
// #### this in incomplete
illegalNames.insert(QStringLiteral("Math"));
illegalNames.insert(QStringLiteral("Array"));
illegalNames.insert(QStringLiteral("String"));
illegalNames.insert(QStringLiteral("Function"));
illegalNames.insert(QStringLiteral("Boolean"));
illegalNames.insert(QStringLiteral("Number"));
illegalNames.insert(QStringLiteral("Date"));
illegalNames.insert(QStringLiteral("RegExp"));
illegalNames.insert(QStringLiteral("Error"));
illegalNames.insert(QStringLiteral("Object"));
for (const char **g = QV4::Compiler::Codegen::s_globalNames; *g != nullptr; ++g)
illegalNames.insert(QString::fromLatin1(*g));
}
struct Error
@ -212,16 +203,14 @@ static bool compileQmlFile(const QString &inputFileName, SaveFunction saveFuncti
QmlIR::JSCodeGen v4CodeGen(irDocument.code,
&irDocument.jsGenerator, &irDocument.jsModule,
&irDocument.jsParserEngine, irDocument.program,
/*import cache*/nullptr, &irDocument.jsGenerator.stringTable, illegalNames);
&irDocument.jsGenerator.stringTable, illegalNames);
v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
for (QmlIR::Object *object: qAsConst(irDocument.objects)) {
if (object->functionsAndExpressions->count == 0)
continue;
QList<QmlIR::CompiledFunctionOrExpression> functionsToCompile;
for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) {
foe->disableAcceleratedLookups = true;
for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next)
functionsToCompile << *foe;
}
const QVector<int> runtimeFunctionIndices = v4CodeGen.generateJSCodeForFunctionsAndBindings(functionsToCompile);
QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
if (!jsErrors.isEmpty()) {
@ -319,8 +308,7 @@ static bool compileJSFile(const QString &inputFileName, const QString &inputFile
{
QmlIR::JSCodeGen v4CodeGen(irDocument.code, &irDocument.jsGenerator,
&irDocument.jsModule, &irDocument.jsParserEngine,
irDocument.program, /*import cache*/nullptr,
&irDocument.jsGenerator.stringTable, illegalNames);
irDocument.program, &irDocument.jsGenerator.stringTable, illegalNames);
v4CodeGen.setUseFastLookups(false); // Disable lookups in non-standalone (aka QML) mode
v4CodeGen.generateFromProgram(inputFileName, inputFileUrl, sourceCode, program,
&irDocument.jsModule, QV4::Compiler::ContextType::ScriptImportedByQML);

Some files were not shown because too many files have changed in this diff Show More