aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp2
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h2
-rw-r--r--src/qml/animations/qanimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qanimationjobutil_p.h2
-rw-r--r--src/qml/animations/qcontinuinganimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qparallelanimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qpauseanimationjob_p.h2
-rw-r--r--src/qml/animations/qsequentialanimationgroupjob_p.h2
-rw-r--r--src/qml/compiler/compiler.pri6
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp109
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h23
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h28
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp12
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h3
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp25
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h2
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp48
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h76
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp529
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h113
-rw-r--r--src/qml/compiler/qv4codegen.cpp1985
-rw-r--r--src/qml/compiler/qv4codegen_p.h106
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_unix.cpp12
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_win.cpp20
-rw-r--r--src/qml/compiler/qv4compileddata.cpp139
-rw-r--r--src/qml/compiler/qv4compileddata_p.h172
-rw-r--r--src/qml/compiler/qv4compiler.cpp173
-rw-r--r--src/qml/compiler/qv4compiler_p.h8
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp254
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h119
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h412
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp495
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h51
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp151
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h226
-rw-r--r--src/qml/configure.json57
-rw-r--r--src/qml/debugger/qqmlabstractprofileradapter_p.h6
-rw-r--r--src/qml/debugger/qqmldebugconnector.cpp2
-rw-r--r--src/qml/debugger/qqmlprofiler.cpp9
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h2
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc4
-rw-r--r--src/qml/doc/src/cppintegration/exposecppattributes.qdoc3
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc46
-rw-r--r--src/qml/jit/jit.pri6
-rw-r--r--src/qml/jit/qv4assembler.cpp158
-rw-r--r--src/qml/jit/qv4assembler_p.h15
-rw-r--r--src/qml/jit/qv4baselinejit.cpp (renamed from src/qml/jit/qv4jit.cpp)838
-rw-r--r--src/qml/jit/qv4baselinejit_p.h (renamed from src/qml/jit/qv4jit_p.h)110
-rw-r--r--src/qml/jit/qv4jithelpers.cpp162
-rw-r--r--src/qml/jit/qv4jithelpers_p.h90
-rw-r--r--src/qml/jsapi/qjsengine.cpp79
-rw-r--r--src/qml/jsapi/qjsengine.h2
-rw-r--r--src/qml/jsapi/qjsvalue.cpp38
-rw-r--r--src/qml/jsapi/qjsvalue_p.h2
-rw-r--r--src/qml/jsapi/qjsvalueiterator.cpp12
-rw-r--r--src/qml/jsruntime/jsruntime.pri43
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp130
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h25
-rw-r--r--src/qml/jsruntime/qv4arraybuffer.cpp16
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h4
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp91
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h30
-rw-r--r--src/qml/jsruntime/qv4arrayiterator.cpp106
-rw-r--r--src/qml/jsruntime/qv4arrayiterator_p.h106
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp538
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h16
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4booleanobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4context.cpp122
-rw-r--r--src/qml/jsruntime/qv4context_p.h90
-rw-r--r--src/qml/jsruntime/qv4dataview.cpp60
-rw-r--r--src/qml/jsruntime/qv4dataview_p.h4
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp137
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h9
-rw-r--r--src/qml/jsruntime/qv4engine.cpp500
-rw-r--r--src/qml/jsruntime/qv4engine_p.h154
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h15
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp24
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h37
-rw-r--r--src/qml/jsruntime/qv4estable.cpp181
-rw-r--r--src/qml/jsruntime/qv4estable_p.h87
-rw-r--r--src/qml/jsruntime/qv4function.cpp52
-rw-r--r--src/qml/jsruntime/qv4function_p.h24
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp349
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h92
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp252
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h149
-rw-r--r--src/qml/jsruntime/qv4global_p.h22
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4globalobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4identifier.cpp86
-rw-r--r--src/qml/jsruntime/qv4identifier_p.h48
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp210
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h50
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp560
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h128
-rw-r--r--src/qml/jsruntime/qv4iterator.cpp64
-rw-r--r--src/qml/jsruntime/qv4iterator_p.h82
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h10
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp20
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp86
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h50
-rw-r--r--src/qml/jsruntime/qv4managed.cpp61
-rw-r--r--src/qml/jsruntime/qv4managed_p.h124
-rw-r--r--src/qml/jsruntime/qv4mapiterator.cpp107
-rw-r--r--src/qml/jsruntime/qv4mapiterator_p.h104
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp279
-rw-r--r--src/qml/jsruntime/qv4mapobject_p.h118
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp239
-rw-r--r--src/qml/jsruntime/qv4mathobject_p.h16
-rw-r--r--src/qml/jsruntime/qv4memberdata_p.h12
-rw-r--r--src/qml/jsruntime/qv4numberobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4numberobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4object.cpp830
-rw-r--r--src/qml/jsruntime/qv4object_p.h250
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp34
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h25
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp201
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h23
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp6
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp9
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h2
-rw-r--r--src/qml/jsruntime/qv4property_p.h13
-rw-r--r--src/qml/jsruntime/qv4propertykey.cpp70
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h143
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp553
-rw-r--r--src/qml/jsruntime/qv4proxy_p.h120
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp54
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h4
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp101
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h19
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp260
-rw-r--r--src/qml/jsruntime/qv4reflect_p.h89
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp21
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h10
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp743
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h1
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h36
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen.cpp9
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h105
-rw-r--r--src/qml/jsruntime/qv4script.cpp20
-rw-r--r--src/qml/jsruntime/qv4script_p.h17
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp71
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4serialize.cpp26
-rw-r--r--src/qml/jsruntime/qv4setiterator.cpp98
-rw-r--r--src/qml/jsruntime/qv4setiterator_p.h103
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp248
-rw-r--r--src/qml/jsruntime/qv4setobject_p.h115
-rw-r--r--src/qml/jsruntime/qv4sparsearray.cpp2
-rw-r--r--src/qml/jsruntime/qv4sparsearray_p.h3
-rw-r--r--src/qml/jsruntime/qv4stackframe.cpp74
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h196
-rw-r--r--src/qml/jsruntime/qv4string.cpp41
-rw-r--r--src/qml/jsruntime/qv4string_p.h113
-rw-r--r--src/qml/jsruntime/qv4stringiterator.cpp95
-rw-r--r--src/qml/jsruntime/qv4stringiterator_p.h103
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp318
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h22
-rw-r--r--src/qml/jsruntime/qv4symbol.cpp182
-rw-r--r--src/qml/jsruntime/qv4symbol_p.h127
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp150
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h44
-rw-r--r--src/qml/jsruntime/qv4util_p.h20
-rw-r--r--src/qml/jsruntime/qv4value.cpp56
-rw-r--r--src/qml/jsruntime/qv4value_p.h163
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4variantobject_p.h3
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp479
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h9
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h221
-rw-r--r--src/qml/memory/qv4heap_p.h99
-rw-r--r--src/qml/memory/qv4mm.cpp62
-rw-r--r--src/qml/memory/qv4mm_p.h228
-rw-r--r--src/qml/parser/parser.pri16
-rw-r--r--src/qml/parser/qqmljs.g4602
-rw-r--r--src/qml/parser/qqmljsast.cpp551
-rw-r--r--src/qml/parser/qqmljsast_p.h884
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h30
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h71
-rw-r--r--src/qml/parser/qqmljsengine_p.cpp7
-rw-r--r--src/qml/parser/qqmljsengine_p.h28
-rw-r--r--src/qml/parser/qqmljsgrammar.cpp1167
-rw-r--r--src/qml/parser/qqmljsgrammar_p.h215
-rw-r--r--src/qml/parser/qqmljskeywords_p.h131
-rw-r--r--src/qml/parser/qqmljslexer.cpp762
-rw-r--r--src/qml/parser/qqmljslexer_p.h88
-rw-r--r--src/qml/parser/qqmljsmemorypool_p.h15
-rw-r--r--src/qml/parser/qqmljsparser.cpp2010
-rw-r--r--src/qml/parser/qqmljsparser_p.h258
-rw-r--r--src/qml/qml.pro4
-rw-r--r--src/qml/qml/ftw/qflagpointer_p.h8
-rw-r--r--src/qml/qml/ftw/qqmlrefcount_p.h20
-rw-r--r--src/qml/qml/qml.pri21
-rw-r--r--src/qml/qml/qqml.h2
-rw-r--r--src/qml/qml/qqmlbinding.cpp11
-rw-r--r--src/qml/qml/qqmlbinding_p.h3
-rw-r--r--src/qml/qml/qqmlcomponent.cpp30
-rw-r--r--src/qml/qml/qqmlcomponent_p.h7
-rw-r--r--src/qml/qml/qqmldata_p.h2
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp4
-rw-r--r--src/qml/qml/qqmldirparser.cpp9
-rw-r--r--src/qml/qml/qqmldirparser_p.h3
-rw-r--r--src/qml/qml/qqmlengine.cpp94
-rw-r--r--src/qml/qml/qqmlengine.h16
-rw-r--r--src/qml/qml/qqmlguard_p.h28
-rw-r--r--src/qml/qml/qqmlimport.cpp20
-rw-r--r--src/qml/qml/qqmlimport_p.h3
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp10
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h17
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp55
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h7
-rw-r--r--src/qml/qml/qqmllocale.cpp6
-rw-r--r--src/qml/qml/qqmllocale_p.h2
-rw-r--r--src/qml/qml/qqmlloggingcategory.cpp36
-rw-r--r--src/qml/qml/qqmlloggingcategory_p.h13
-rw-r--r--src/qml/qml/qqmlmetatype.cpp59
-rw-r--r--src/qml/qml/qqmlmetatype_p.h7
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp41
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h4
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp122
-rw-r--r--src/qml/qml/qqmlopenmetaobject_p.h5
-rw-r--r--src/qml/qml/qqmlproperty.cpp2
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h6
-rw-r--r--src/qml/qml/qqmltypeloader.cpp130
-rw-r--r--src/qml/qml/qqmltypeloader_p.h41
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp84
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h16
-rw-r--r--src/qml/qml/qqmlvaluetypeproxybinding_p.h2
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp47
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h10
-rw-r--r--src/qml/qml/qqmlvme_p.h4
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp6
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h6
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp110
-rw-r--r--src/qml/qml/qqmlxmlhttprequest_p.h4
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp39
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h6
-rw-r--r--src/qml/qml/v8/qv8engine.cpp37
-rw-r--r--src/qml/qml/v8/qv8engine_p.h10
-rw-r--r--src/qml/qtqmlglobal.h1
-rw-r--r--src/qml/qtqmlglobal_p.h2
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp347
-rw-r--r--src/qml/types/qqmldelegatemodel_p.h10
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h26
-rw-r--r--src/qml/types/qqmllistmodel.cpp171
-rw-r--r--src/qml/types/qqmllistmodel_p.h4
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h40
-rw-r--r--src/qml/types/qqmllistmodelworkeragent_p.h2
-rw-r--r--src/qml/types/qqmlmodelsmodule.cpp5
-rw-r--r--src/qml/types/qqmlobjectmodel_p.h2
-rw-r--r--src/qml/types/qqmltableinstancemodel.cpp341
-rw-r--r--src/qml/types/qqmltableinstancemodel_p.h132
-rw-r--r--src/qml/types/qqmltimer_p.h2
-rw-r--r--src/qml/types/qquickworkerscript.cpp12
-rw-r--r--src/qml/types/types.pri24
-rw-r--r--src/qml/util/qqmladaptormodel.cpp177
-rw-r--r--src/qml/util/qqmladaptormodel_p.h26
-rw-r--r--src/qml/util/qqmlpropertymap.cpp4
259 files changed, 20673 insertions, 12867 deletions
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index 3f33e7e81b..4e82c7a062 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -78,7 +78,7 @@ QQmlAnimationTimer *QQmlAnimationTimer::instance(bool create)
inst = animationTimer() ? animationTimer()->localData() : 0;
}
#else
- static QAnimationTimer unifiedTimer;
+ static QQmlAnimationTimer unifiedTimer;
inst = &unifiedTimer;
#endif
return inst;
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index 63fd4b0dac..0be6ca96ea 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -56,6 +56,8 @@
#include <QtCore/private/qabstractanimation_p.h>
#include <vector>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class QAnimationGroupJob;
diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h
index fb567dc019..b01b2f3b36 100644
--- a/src/qml/animations/qanimationgroupjob_p.h
+++ b/src/qml/animations/qanimationgroupjob_p.h
@@ -54,6 +54,8 @@
#include "private/qabstractanimationjob_p.h"
#include <QtCore/qdebug.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob
diff --git a/src/qml/animations/qanimationjobutil_p.h b/src/qml/animations/qanimationjobutil_p.h
index 0bb9e83b2d..e3d6fe9178 100644
--- a/src/qml/animations/qanimationjobutil_p.h
+++ b/src/qml/animations/qanimationjobutil_p.h
@@ -51,6 +51,8 @@
// We mean it.
//
+QT_REQUIRE_CONFIG(qml_animation);
+
#define RETURN_IF_DELETED(func) \
{ \
bool *prevWasDeleted = m_wasDeleted; \
diff --git a/src/qml/animations/qcontinuinganimationgroupjob_p.h b/src/qml/animations/qcontinuinganimationgroupjob_p.h
index baf4ff1ae5..c67b8d39ad 100644
--- a/src/qml/animations/qcontinuinganimationgroupjob_p.h
+++ b/src/qml/animations/qcontinuinganimationgroupjob_p.h
@@ -53,6 +53,8 @@
#include "private/qanimationgroupjob_p.h"
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QContinuingAnimationGroupJob : public QAnimationGroupJob
diff --git a/src/qml/animations/qparallelanimationgroupjob_p.h b/src/qml/animations/qparallelanimationgroupjob_p.h
index 67ba626247..0265fe3274 100644
--- a/src/qml/animations/qparallelanimationgroupjob_p.h
+++ b/src/qml/animations/qparallelanimationgroupjob_p.h
@@ -53,6 +53,8 @@
#include "private/qanimationgroupjob_p.h"
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob
diff --git a/src/qml/animations/qpauseanimationjob_p.h b/src/qml/animations/qpauseanimationjob_p.h
index d0e8d57fc7..6c9bbf0dab 100644
--- a/src/qml/animations/qpauseanimationjob_p.h
+++ b/src/qml/animations/qpauseanimationjob_p.h
@@ -53,6 +53,8 @@
#include <private/qanimationgroupjob_p.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QPauseAnimationJob : public QAbstractAnimationJob
diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h
index 800f0c3b90..13f9806be1 100644
--- a/src/qml/animations/qsequentialanimationgroupjob_p.h
+++ b/src/qml/animations/qsequentialanimationgroupjob_p.h
@@ -53,6 +53,8 @@
#include <private/qanimationgroupjob_p.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class QPauseAnimationJob;
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index 95096db51d..da3c173545 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -11,7 +11,8 @@ HEADERS += \
$$PWD/qv4codegen_p.h \
$$PWD/qqmlirbuilder_p.h \
$$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4instr_moth_p.h
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4bytecodehandler_p.h
SOURCES += \
$$PWD/qv4bytecodegenerator.cpp \
@@ -21,7 +22,8 @@ SOURCES += \
$$PWD/qv4compilerscanfunctions.cpp \
$$PWD/qv4codegen.cpp \
$$PWD/qqmlirbuilder.cpp \
- $$PWD/qv4instr_moth.cpp
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4bytecodehandler.cpp
!qmldevtools_build {
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 8a1b3744ee..820f127331 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -96,23 +96,24 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons
declarationsOverride = nullptr;
}
-QString Object::sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
+QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
{
QSet<int> functionNames;
- for (Function *f = functions->first; f; f = f->next) {
- QQmlJS::AST::FunctionDeclaration *function = f->functionDeclaration;
- Q_ASSERT(function);
- *errorLocation = function->identifierToken;
+ for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) {
+ Function *f = functionit.ptr;
+ errorLocation->startLine = f->location.line;
+ errorLocation->startColumn = f->location.column;
if (functionNames.contains(f->nameIndex))
return tr("Duplicate method name");
functionNames.insert(f->nameIndex);
- for (QmlIR::Signal *s = qmlSignals->first; s; s = s->next) {
+ for (auto signalit = obj->signalsBegin(); signalit != obj->signalsEnd(); ++signalit) {
+ QmlIR::Signal *s = signalit.ptr;
if (s->nameIndex == f->nameIndex)
return tr("Duplicate method name");
}
- const QString name = function->name.toString();
+ const QString name = stringAt(f->nameIndex);
if (name.at(0).isUpper())
return tr("Method names cannot begin with an upper case letter");
if (illegalNames.contains(name))
@@ -497,7 +498,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectBinding *node)
bool IRBuilder::visit(QQmlJS::AST::UiScriptBinding *node)
{
- appendBinding(node->qualifiedId, node->statement);
+ appendBinding(node->qualifiedId, node->statement, node);
return false;
}
@@ -601,7 +602,7 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu
return false;
QQmlJS::AST::SourceLocation loc;
- QString error = obj->sanityCheckFunctionNames(illegalNames, &loc);
+ QString error = sanityCheckFunctionNames(obj, illegalNames, &loc);
if (!error.isEmpty()) {
recordError(loc, error);
return false;
@@ -957,7 +958,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
QQmlJS::AST::Node::accept(node->binding, this);
} else if (node->statement) {
if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement))
- appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement, node);
}
qSwap(_propertyDeclaration, property);
}
@@ -971,26 +972,27 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) {
CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
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>();
- f->functionDeclaration = funDecl;
QQmlJS::AST::SourceLocation loc = funDecl->identifierToken;
f->location.line = loc.startLine;
f->location.column = loc.startColumn;
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
- int formalsCount = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next)
- ++formalsCount;
+ const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList();
+ int formalsCount = formals.size();
f->formals.allocate(pool, formalsCount);
int i = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i)
- f->formals[i] = registerString(it->name.toString());
+ for (const QString &arg : formals) {
+ f->formals[i] = registerString(arg);
+ ++i;
+ }
_object->appendFunction(f);
} else {
@@ -1044,7 +1046,7 @@ QStringRef IRBuilder::textRefAt(const QQmlJS::AST::SourceLocation &first, const
return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset);
}
-void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement)
+void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode)
{
QQmlJS::AST::SourceLocation loc = statement->firstSourceLocation();
binding->valueLocation.line = loc.startLine;
@@ -1090,6 +1092,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
expr->node = statement;
+ expr->parentNode = parentNode;
expr->nameIndex = registerString(QLatin1String("expression for ")
+ stringAt(binding->propertyNameIndex));
expr->disableAcceleratedLookups = false;
@@ -1216,7 +1219,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
}
}
-void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value)
+void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken;
Object *object = nullptr;
@@ -1227,7 +1230,7 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Sta
return;
}
qSwap(_object, object);
- appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value);
+ appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value, parentNode);
qSwap(_object, object);
}
@@ -1242,7 +1245,8 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex,
qSwap(_object, object);
}
-void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value)
+void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex,
+ QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
@@ -1250,7 +1254,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
binding->flags = 0;
- setBindingValue(binding, value);
+ setBindingValue(binding, value, parentNode);
QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
if (!error.isEmpty()) {
recordError(qualifiedNameLocation, error);
@@ -1806,24 +1810,36 @@ void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
{
+ auto qmlName = [&](const CompiledFunctionOrExpression &c) {
+ if (c.nameIndex != 0)
+ return stringPool->stringForIndex(c.nameIndex);
+ else
+ return QStringLiteral("%qml-expression-entry");
+ };
QVector<int> runtimeFunctionIndices(functions.size());
- QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode);
- scan.enterGlobalEnvironment(QV4::Compiler::QmlBinding);
+ QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global);
+ scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding);
for (const CompiledFunctionOrExpression &f : functions) {
Q_ASSERT(f.node != qmlRoot);
+ Q_ASSERT(f.parentNode && f.parentNode != qmlRoot);
QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node);
- if (function)
+ if (function) {
scan.enterQmlFunction(function);
- else
- scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding);
+ } else {
+ Q_ASSERT(f.node != f.parentNode);
+ scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
+ }
scan(function ? function->body : f.node);
scan.leaveEnvironment();
}
scan.leaveEnvironment();
+ if (hasError)
+ return QVector<int>();
+
_context = nullptr;
for (int i = 0; i < functions.count(); ++i) {
@@ -1836,15 +1852,13 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QString name;
if (function)
name = function->name.toString();
- else if (qmlFunction.nameIndex != 0)
- name = stringPool->stringForIndex(qmlFunction.nameIndex);
else
- name = QStringLiteral("%qml-expression-entry");
+ name = qmlName(qmlFunction);
- QQmlJS::AST::SourceElements *body;
- if (function)
- body = function->body ? function->body->elements : nullptr;
- else {
+ QQmlJS::AST::StatementList *body;
+ if (function) {
+ body = function->body;
+ } else {
// Synthesize source elements.
QQmlJS::MemoryPool *pool = jsEngine->pool();
@@ -1854,13 +1868,12 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QQmlJS::AST::ExpressionNode *expr = node->expressionCast();
stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr);
}
- QQmlJS::AST::SourceElement *element = new (pool) QQmlJS::AST::StatementSourceElement(stmt);
- body = new (pool) QQmlJS::AST::SourceElements(element);
+ body = new (pool) QQmlJS::AST::StatementList(stmt);
body = body->finish();
}
_disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
- int idx = defineFunction(name, node,
+ int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
function ? function->formals : nullptr,
body);
runtimeFunctionIndices[i] = idx;
@@ -1869,7 +1882,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
-int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body)
+int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body)
{
int qmlContextTemp = -1;
int importedScriptsTemp = -1;
@@ -2190,7 +2203,7 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
- if (_context->compilationMode == QV4::Compiler::QmlBinding)
+ if (_context->contextType == QV4::Compiler::ContextType::Binding)
_context->idObjectDependencies.insert(mapping.idIndex);
Instruction::LoadIdObject load;
@@ -2446,8 +2459,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
}
}
- QQmlJS::Engine *jsParserEngine = &output->jsParserEngine;
-
const quint32_le *functionIdx = serializedObject->functionOffsetTable();
for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) {
QmlIR::Function *f = pool->New<QmlIR::Function>();
@@ -2458,26 +2469,10 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
f->location = compiledFunction->location;
f->nameIndex = compiledFunction->nameIndex;
- QQmlJS::AST::FormalParameterList *paramList = nullptr;
- const quint32_le *formalNameIdx = compiledFunction->formalsTable();
- for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) {
- const QString formal = unit->stringAt(*formalNameIdx);
- QStringRef paramNameRef = jsParserEngine->newStringRef(formal);
-
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
- }
-
- if (paramList)
- paramList = paramList->finish();
-
const QString name = unit->stringAt(compiledFunction->nameIndex);
- f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/nullptr);
f->formals.allocate(pool, int(compiledFunction->nFormals));
- formalNameIdx = compiledFunction->formalsTable();
+ const quint32_le *formalNameIdx = compiledFunction->formalsTable();
for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
f->formals[i] = *formalNameIdx;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 689b232b1c..f8d481e14f 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -57,7 +57,6 @@
#include <private/qqmljsmemorypool_p.h>
#include <private/qv4codegen_p.h>
#include <private/qv4compiler_p.h>
-#include <private/qqmljslexer_p.h>
#include <QTextStream>
#include <QCoreApplication>
@@ -325,7 +324,6 @@ struct Alias : public QV4::CompiledData::Alias
struct Function
{
- QQmlJS::AST::FunctionDeclaration *functionDeclaration;
QV4::CompiledData::Location location;
int nameIndex;
quint32 index; // index in parsedQML::functions
@@ -342,12 +340,9 @@ struct Function
struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
{
CompiledFunctionOrExpression()
-
{}
- CompiledFunctionOrExpression(QQmlJS::AST::Node *n)
- : node(n)
- {}
+ QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression
QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression
quint32 nameIndex = 0;
bool disableAcceleratedLookups = false;
@@ -400,8 +395,6 @@ public:
void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation());
- QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
-
QString appendEnum(Enum *enumeration);
QString appendSignal(Signal *signal);
QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation);
@@ -520,12 +513,12 @@ public:
QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first,
const QQmlJS::AST::SourceLocation &last) const;
- void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement);
+ void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode);
void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding);
- void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value);
+ void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, AST::Node *parentNode);
void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false);
- void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value);
+ void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode);
void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
bool appendAlias(QQmlJS::AST::UiPublicMember *node);
@@ -548,6 +541,8 @@ public:
static bool isStatementNodeScript(QQmlJS::AST::Statement *statement);
static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement);
+ QString sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
+
QList<QQmlJS::DiagnosticMessage> errors;
QSet<QString> illegalNames;
@@ -578,7 +573,7 @@ private:
#ifndef V4_BOOTSTRAP
struct Q_QML_EXPORT PropertyResolver
{
- PropertyResolver(const QQmlPropertyCache *cache)
+ PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache)
: cache(cache)
{}
@@ -597,7 +592,7 @@ struct Q_QML_EXPORT PropertyResolver
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
- const QQmlPropertyCache *cache;
+ QQmlRefPointer<QQmlPropertyCache> cache;
};
#endif
@@ -623,7 +618,7 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body) override;
+ AST::StatementList *body) override;
protected:
void beginFunctionBodyHook() override;
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
index 8bbc8291b4..02517ea6bb 100644
--- a/src/qml/compiler/qqmlpropertycachecreator_p.h
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -99,8 +99,8 @@ public:
protected:
QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
- QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
- QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache);
+ QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
+ QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
QString stringAt(int index) const { return objectContainer->stringAt(index); }
@@ -152,7 +152,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
- QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
if (error.isSet())
return error;
@@ -166,7 +166,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
}
- QQmlPropertyCache *baseTypeCache;
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
{
QQmlCompileError error;
baseTypeCache = propertyCacheForObject(obj, context, &error);
@@ -209,7 +209,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
template <typename ObjectContainer>
-inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
+inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
{
if (context.instantiatingProperty) {
return context.instantiatingPropertyCache(enginePrivate);
@@ -241,14 +241,12 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
@@ -264,7 +262,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache)
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
{
QQmlRefPointer<QQmlPropertyCache> cache;
cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
@@ -314,7 +312,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
}
}
if (newClassName.isEmpty()) {
- newClassName = QQmlMetaObject(baseTypeCache).className();
+ newClassName = QQmlMetaObject(baseTypeCache.data()).className();
newClassName.append("_QML_");
newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
}
@@ -354,7 +352,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
// and throw an error if there is a signal/method defined as an override.
QSet<QString> seenSignals;
seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
- QQmlPropertyCache *parentCache = cache;
+ QQmlPropertyCache *parentCache = cache.data();
while ((parentCache = parentCache->parent())) {
if (int pSigCount = parentCache->signalCount()) {
int pSigOffset = parentCache->signalOffset();
@@ -440,15 +438,13 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
paramTypes[i + 1] = compilationUnit->metaTypeId;
-
- tdata->release();
} else {
paramTypes[i + 1] = qmltype.typeId();
}
@@ -523,7 +519,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
Q_ASSERT(qmltype.isValid());
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
@@ -534,8 +530,6 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
} else {
propertyType = compilationUnit->listMetaTypeId;
}
-
- tdata->release();
} else {
if (p->type == QV4::CompiledData::Property::Custom) {
propertyType = qmltype.typeId();
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
index ffd3b5975a..afa2e4ad81 100644
--- a/src/qml/compiler/qqmlpropertyvalidator.cpp
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -45,8 +45,9 @@
QT_BEGIN_NAMESPACE
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit)
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
: enginePrivate(enginePrivate)
+ , compilationUnit(compilationUnit)
, imports(imports)
, qmlUnit(compilationUnit->data)
, resolvedTypes(compilationUnit->resolvedTypes)
@@ -629,7 +630,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex);
if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) {
- QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
const QMetaObject *mo = cache->firstCppMetaObject();
QQmlType qmlType;
while (mo && !qmlType.isValid()) {
@@ -670,7 +671,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
} else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) {
return noError;
} else if (QQmlValueTypeFactory::isValueType(property->propType())) {
- return QQmlCompileError(binding->location, tr("Unexpected object assignment"));
+ return QQmlCompileError(binding->location, tr("Unexpected object assignment for property \"%1\"").arg(propertyName));
} else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
} else {
@@ -680,7 +681,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
// Using -1 for the minor version ensures that we get the raw metaObject.
QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1);
- // Will be true if the assgned type inherits propertyMetaObject
+ // Will be true if the assigned type inherits propertyMetaObject
bool isAssignable = false;
// Determine isAssignable value
if (propertyMetaObject) {
@@ -692,7 +693,8 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
}
if (!isAssignable) {
- return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property"));
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
+ .arg(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType()))));
}
}
return noError;
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h
index e37b8141f4..a8f75a0a7e 100644
--- a/src/qml/compiler/qqmlpropertyvalidator_p.h
+++ b/src/qml/compiler/qqmlpropertyvalidator_p.h
@@ -58,7 +58,7 @@ class QQmlPropertyValidator
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit);
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
QVector<QQmlCompileError> validate();
@@ -74,6 +74,7 @@ private:
QString stringAt(int index) const { return qmlUnit->stringAt(index); }
QQmlEnginePrivate *enginePrivate;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
const QQmlImports &imports;
const QV4::CompiledData::Unit *qmlUnit;
const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index a896745b3f..37271e2c54 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -67,7 +67,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type
{
}
-QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
{
// Build property caches and VME meta object data
@@ -147,7 +147,7 @@ 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, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
+ document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
v4CodeGenerator.setUseFastLookups(false);
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
@@ -165,7 +165,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
// The js unit owns the data and will free the qml unit.
document->javaScriptCompilationUnit->data = qmlUnit;
- QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit;
compilationUnit = document->javaScriptCompilationUnit;
compilationUnit->typeNameCache = typeNameCache;
compilationUnit->resolvedTypes = resolvedTypes;
@@ -339,14 +339,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
if (!type.isValid()) {
if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) {
if (type.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
@@ -466,14 +464,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
for (const QString &param : qAsConst(parameters)) {
QStringRef paramNameRef = compiler->newStringRef(param);
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
+ QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr);
+ paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b);
}
if (paramList)
- paramList = paramList->finish();
+ paramList = paramList->finish(pool);
QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr;
@@ -490,11 +486,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
}
if (!functionDeclaration) {
QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
- QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement);
- QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement);
- elements = elements->finish();
-
- QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
+ QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement);
+ body = body->finish();
functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
functionDeclaration->lbraceToken = functionDeclaration->functionToken
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index b8eddcb9b2..537f87ab4c 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -92,7 +92,7 @@ public:
QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes;
// ---
- QV4::CompiledData::CompilationUnit *compile();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile();
QList<QQmlError> compilationErrors() const { return errors; }
void recordError(QQmlError error);
diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp
index 4d50654d27..7e1f49ee86 100644
--- a/src/qml/compiler/qv4bytecodegenerator.cpp
+++ b/src/qml/compiler/qv4bytecodegenerator.cpp
@@ -70,14 +70,14 @@ int BytecodeGenerator::newRegisterArray(int n)
void BytecodeGenerator::packInstruction(I &i)
{
- uchar type = *reinterpret_cast<uchar *>(i.packed);
- Q_ASSERT(type >= MOTH_NUM_INSTRUCTIONS());
- if (type >= MOTH_NUM_INSTRUCTIONS())
- type -= MOTH_NUM_INSTRUCTIONS();
+ Instr::Type type = Instr::unpack(i.packed);
+ Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
+ type = Instr::narrowInstructionType(type);
int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
+ uchar *code = i.packed + Instr::encodedLength(type);
for (int j = 0; j < nMembers; ++j) {
- instructionsAsInts[j] = qFromLittleEndian<qint32>(i.packed + 1 + j * sizeof(int));
+ instructionsAsInts[j] = qFromLittleEndian<qint32>(code + j * sizeof(int));
}
enum {
Normal,
@@ -89,11 +89,10 @@ void BytecodeGenerator::packInstruction(I &i)
break;
}
}
- char *code = i.packed;
+ code = i.packed;
switch (width) {
case Normal:
- *reinterpret_cast<uchar *>(code) = type;
- ++code;
+ code = Instr::pack(code, type);
for (int n = 0; n < nMembers; ++n) {
qint8 v = static_cast<qint8>(instructionsAsInts[n]);
memcpy(code, &v, 1);
@@ -122,7 +121,7 @@ void BytecodeGenerator::adjustJumpOffsets()
// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
uchar type = *reinterpret_cast<const uchar *>(i.packed);
- if (type >= MOTH_NUM_INSTRUCTIONS()) {
+ if (Instr::isWide(Instr::Type(type))) {
Q_ASSERT(i.offsetForJump == i.size - 4);
qToLittleEndian<qint32>(jumpOffset, c);
} else {
@@ -177,7 +176,7 @@ void BytecodeGenerator::finalize(Compiler::Context *context)
entry.line = currentLine;
lineNumbers.append(entry);
}
- code.append(i.packed, i.size);
+ code.append(reinterpret_cast<const char *>(i.packed), i.size);
}
context->code = code;
@@ -185,6 +184,25 @@ void BytecodeGenerator::finalize(Compiler::Context *context)
}
int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
+ if (lastInstrType == int(Instr::Type::StoreReg)) {
+ if (type == Instr::Type::LoadReg) {
+ if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
+ // value is already in the accumulator
+ return -1;
+ }
+ }
+ if (type == Instr::Type::MoveReg) {
+ if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
+ Instruction::StoreReg store;
+ store.reg = i.MoveReg.destReg;
+ addInstruction(store);
+ return -1;
+ }
+ }
+ }
+ lastInstrType = int(type);
+ lastInstr = i;
+
#if QT_CONFIG(qml_debug)
if (debugMode && type != Instr::Type::Debug) {
QT_WARNING_PUSH
@@ -207,12 +225,10 @@ QT_WARNING_POP
const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
int s = argCount*sizeof(int);
if (offsetOfOffset != -1)
- offsetOfOffset += 1;
- I instr{type, static_cast<short>(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" };
- char *code = instr.packed;
- *reinterpret_cast<uchar *>(code) = static_cast<uchar>(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type));
- ++code;
- Q_ASSERT(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type) < 256);
+ offsetOfOffset += Instr::encodedLength(type);
+ I instr{type, static_cast<short>(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" };
+ uchar *code = instr.packed;
+ code = Instr::pack(code, Instr::wideInstructionType(type));
for (int j = 0; j < argCount; ++j) {
qToLittleEndian<qint32>(i.argumentsAsInts[j], code);
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index e69f2cd310..dca5771356 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -77,22 +77,18 @@ public:
Label(BytecodeGenerator *generator, LinkMode mode = LinkNow)
: generator(generator),
index(generator->labels.size()) {
- generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1);
- }
- static Label returnLabel() {
- Label l;
- l.index = INT_MAX;
- return l;
- }
- bool isReturn() const {
- return index == INT_MAX;
+ generator->labels.append(-1);
+ if (mode == LinkNow)
+ link();
}
void link() {
Q_ASSERT(index >= 0);
Q_ASSERT(generator->labels[index] == -1);
generator->labels[index] = generator->instructions.size();
+ generator->clearLastInstruction();
}
+ bool isValid() const { return generator != nullptr; }
BytecodeGenerator *generator = nullptr;
int index = -1;
@@ -133,14 +129,16 @@ public:
};
struct ExceptionHandler : public Label {
+ ExceptionHandler() = default;
ExceptionHandler(BytecodeGenerator *generator)
: Label(generator, LinkLater)
{
}
~ExceptionHandler()
{
- Q_ASSERT(generator->currentExceptionHandler != this);
+ Q_ASSERT(!generator || generator->currentExceptionHandler != this);
}
+ bool isValid() const { return generator != nullptr; }
};
Label label() {
@@ -181,6 +179,18 @@ public:
return addJumpInstruction(data);
}
+ Q_REQUIRED_RESULT Jump jumpNotUndefined()
+ {
+ Instruction::JumpNotUndefined data;
+ return addJumpInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpNoException()
+ {
+ Instruction::JumpNoException data;
+ return addJumpInstruction(data);
+ }
+
void jumpStrictEqual(const StackSlot &lhs, const Label &target)
{
Instruction::CmpStrictEqual cmp;
@@ -197,26 +207,10 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(target);
}
- Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictNotEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- void setExceptionHandler(ExceptionHandler *handler)
+ void setUnwindHandler(ExceptionHandler *handler)
{
currentExceptionHandler = handler;
- Instruction::SetExceptionHandler data;
+ Instruction::SetUnwindHandler data;
data.offset = 0;
if (!handler)
addInstruction(data);
@@ -224,6 +218,19 @@ public:
addJumpInstruction(data).link(*handler);
}
+ void unwindToLabel(int level, const Label &target)
+ {
+ if (level) {
+ Instruction::UnwindToLabel unwind;
+ unwind.level = level;
+ addJumpInstruction(unwind).link(target);
+ } else {
+ jump().link(target);
+ }
+ }
+
+
+
void setLocation(const QQmlJS::AST::SourceLocation &loc);
ExceptionHandler *exceptionHandler() const {
@@ -233,6 +240,7 @@ public:
int newRegister();
int newRegisterArray(int n);
int registerCount() const { return regCount; }
+ int currentRegister() const { return currentReg; }
void finalize(Compiler::Context *context);
@@ -252,6 +260,11 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
}
+ void clearLastInstruction()
+ {
+ lastInstrType = -1;
+ }
+
private:
friend struct Jump;
friend struct Label;
@@ -266,7 +279,7 @@ private:
int line;
int offsetForJump;
int linkedLabel;
- char packed[sizeof(Instr) + 2]; // 2 for instruction and prefix
+ unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type
};
void compressInstructions();
@@ -275,7 +288,7 @@ private:
QVector<I> instructions;
QVector<int> labels;
- ExceptionHandler *currentExceptionHandler;
+ ExceptionHandler *currentExceptionHandler = nullptr;
int regCount = 0;
public:
int currentReg = 0;
@@ -283,6 +296,9 @@ private:
int startLine = 0;
int currentLine = 0;
bool debugMode = false;
+
+ int lastInstrType = -1;
+ Moth::Instr lastInstr;
};
}
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
new file mode 100644
index 0000000000..23f7051718
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -0,0 +1,529 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4bytecodehandler_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace Moth;
+
+ByteCodeHandler::~ByteCodeHandler()
+{
+}
+
+#define DISPATCH_INSTRUCTION(name, nargs, ...) \
+ generate_##name( \
+ __VA_ARGS__ \
+ );
+
+#define DECODE_AND_DISPATCH(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ Q_UNUSED(base_ptr); \
+ startInstruction(Instr::Type::instr); \
+ _offset = code - start; \
+ INSTR_##instr(DISPATCH) \
+ endInstruction(Instr::Type::instr); \
+ continue; \
+ }
+
+void ByteCodeHandler::decode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+
+ FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH)
+ }
+}
+
+#undef DECODE_AND_DISPATCH
+#undef DISPATCH_INSTRUCTION
+
+#define MOTH_UNUSED_ARGS0()
+#define MOTH_UNUSED_ARGS1(arg) \
+ Q_UNUSED(arg);
+#define MOTH_UNUSED_ARGS2(arg1, arg2) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2);
+#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3);
+#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3); \
+ Q_UNUSED(arg4);
+
+#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__))
+
+#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \
+ MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__)
+
+#define COLLECTOR_BEGIN_INSTR(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \
+ Q_UNUSED(base_ptr);
+
+#define COLLECTOR_END_INSTR(instr) \
+ continue; \
+ }
+
+std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ std::vector<int> labels;
+
+ const auto addLabel = [&labels,len](int offset) {
+ Q_ASSERT(offset >= 0 && offset < static_cast<int>(len));
+ labels.push_back(offset);
+ };
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+ Q_UNREACHABLE();
+
+ COLLECTOR_BEGIN_INSTR(LoadReg)
+ COLLECTOR_END_INSTR(LoadReg)
+
+ COLLECTOR_BEGIN_INSTR(StoreReg)
+ COLLECTOR_END_INSTR(StoreReg)
+
+ COLLECTOR_BEGIN_INSTR(MoveReg)
+ COLLECTOR_END_INSTR(MoveReg)
+
+ COLLECTOR_BEGIN_INSTR(LoadConst)
+ COLLECTOR_END_INSTR(LoadConst)
+
+ COLLECTOR_BEGIN_INSTR(LoadNull)
+ COLLECTOR_END_INSTR(LoadNull)
+
+ COLLECTOR_BEGIN_INSTR(LoadZero)
+ COLLECTOR_END_INSTR(LoadZero)
+
+ COLLECTOR_BEGIN_INSTR(LoadTrue)
+ COLLECTOR_END_INSTR(LoadTrue)
+
+ COLLECTOR_BEGIN_INSTR(LoadFalse)
+ COLLECTOR_END_INSTR(LoadFalse)
+
+ COLLECTOR_BEGIN_INSTR(LoadUndefined)
+ COLLECTOR_END_INSTR(LoadUndefined)
+
+ COLLECTOR_BEGIN_INSTR(LoadInt)
+ COLLECTOR_END_INSTR(LoadInt)
+
+ COLLECTOR_BEGIN_INSTR(MoveConst)
+ COLLECTOR_END_INSTR(MoveConst)
+
+ COLLECTOR_BEGIN_INSTR(LoadLocal)
+ COLLECTOR_END_INSTR(LoadLocal)
+
+ COLLECTOR_BEGIN_INSTR(StoreLocal)
+ COLLECTOR_END_INSTR(StoreLocal)
+
+ COLLECTOR_BEGIN_INSTR(LoadScopedLocal)
+ COLLECTOR_END_INSTR(LoadScopedLocal)
+
+ COLLECTOR_BEGIN_INSTR(StoreScopedLocal)
+ COLLECTOR_END_INSTR(StoreScopedLocal)
+
+ COLLECTOR_BEGIN_INSTR(LoadRuntimeString)
+ COLLECTOR_END_INSTR(LoadRuntimeString)
+
+ COLLECTOR_BEGIN_INSTR(MoveRegExp)
+ COLLECTOR_END_INSTR(MoveRegExp)
+
+ COLLECTOR_BEGIN_INSTR(LoadClosure)
+ COLLECTOR_END_INSTR(LoadClosure)
+
+ COLLECTOR_BEGIN_INSTR(LoadName)
+ COLLECTOR_END_INSTR(LoadName)
+
+ COLLECTOR_BEGIN_INSTR(LoadGlobalLookup)
+ COLLECTOR_END_INSTR(LoadGlobalLookup)
+
+ COLLECTOR_BEGIN_INSTR(StoreNameSloppy)
+ COLLECTOR_END_INSTR(StoreNameSloppy)
+
+ COLLECTOR_BEGIN_INSTR(StoreNameStrict)
+ COLLECTOR_END_INSTR(StoreNameStrict)
+
+ COLLECTOR_BEGIN_INSTR(LoadElement)
+ COLLECTOR_END_INSTR(LoadElement)
+
+ COLLECTOR_BEGIN_INSTR(StoreElement)
+ COLLECTOR_END_INSTR(StoreElement)
+
+ COLLECTOR_BEGIN_INSTR(LoadProperty)
+ COLLECTOR_END_INSTR(LoadProperty)
+
+ COLLECTOR_BEGIN_INSTR(GetLookup)
+ COLLECTOR_END_INSTR(GetLookup)
+
+ COLLECTOR_BEGIN_INSTR(StoreProperty)
+ COLLECTOR_END_INSTR(StoreProperty)
+
+ COLLECTOR_BEGIN_INSTR(SetLookup)
+ COLLECTOR_END_INSTR(SetLookup)
+
+ COLLECTOR_BEGIN_INSTR(LoadSuperProperty)
+ COLLECTOR_END_INSTR(LoadSuperProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreSuperProperty)
+ COLLECTOR_END_INSTR(StoreSuperProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreScopeObjectProperty)
+ COLLECTOR_END_INSTR(StoreScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadScopeObjectProperty)
+ COLLECTOR_END_INSTR(LoadScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreContextObjectProperty)
+ COLLECTOR_END_INSTR(StoreContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadContextObjectProperty)
+ COLLECTOR_END_INSTR(LoadContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadIdObject)
+ COLLECTOR_END_INSTR(LoadIdObject)
+
+ COLLECTOR_BEGIN_INSTR(Yield)
+ COLLECTOR_END_INSTR(Yield)
+
+ COLLECTOR_BEGIN_INSTR(Resume)
+ COLLECTOR_END_INSTR(Resume)
+
+ COLLECTOR_BEGIN_INSTR(CallValue)
+ COLLECTOR_END_INSTR(CallValue)
+
+ COLLECTOR_BEGIN_INSTR(CallProperty)
+ COLLECTOR_END_INSTR(CallProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallPropertyLookup)
+ COLLECTOR_END_INSTR(CallPropertyLookup)
+
+ COLLECTOR_BEGIN_INSTR(CallElement)
+ COLLECTOR_END_INSTR(CallElement)
+
+ COLLECTOR_BEGIN_INSTR(CallName)
+ COLLECTOR_END_INSTR(CallName)
+
+ COLLECTOR_BEGIN_INSTR(CallPossiblyDirectEval)
+ COLLECTOR_END_INSTR(CallPossiblyDirectEval)
+
+ COLLECTOR_BEGIN_INSTR(CallGlobalLookup)
+ COLLECTOR_END_INSTR(CallGlobalLookup)
+
+ COLLECTOR_BEGIN_INSTR(CallScopeObjectProperty)
+ COLLECTOR_END_INSTR(CallScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallContextObjectProperty)
+ COLLECTOR_END_INSTR(CallContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallWithSpread)
+ COLLECTOR_END_INSTR(CallWithSpread)
+
+ COLLECTOR_BEGIN_INSTR(Construct)
+ COLLECTOR_END_INSTR(Construct)
+
+ COLLECTOR_BEGIN_INSTR(ConstructWithSpread)
+ COLLECTOR_END_INSTR(ConstructWithSpread)
+
+ COLLECTOR_BEGIN_INSTR(SetUnwindHandler)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(SetUnwindHandler)
+
+ COLLECTOR_BEGIN_INSTR(UnwindDispatch)
+ COLLECTOR_END_INSTR(UnwindDispatch)
+
+ COLLECTOR_BEGIN_INSTR(UnwindToLabel)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(UnwindToLabel)
+
+ COLLECTOR_BEGIN_INSTR(ThrowException)
+ COLLECTOR_END_INSTR(ThrowException)
+
+ COLLECTOR_BEGIN_INSTR(GetException)
+ COLLECTOR_END_INSTR(HasException)
+
+ COLLECTOR_BEGIN_INSTR(SetException)
+ COLLECTOR_END_INSTR(SetExceptionFlag)
+
+ COLLECTOR_BEGIN_INSTR(CreateCallContext)
+ COLLECTOR_END_INSTR(CreateCallContext)
+
+ COLLECTOR_BEGIN_INSTR(PushCatchContext)
+ COLLECTOR_END_INSTR(PushCatchContext)
+
+ COLLECTOR_BEGIN_INSTR(PushWithContext)
+ COLLECTOR_END_INSTR(PushWithContext)
+
+ COLLECTOR_BEGIN_INSTR(PushBlockContext)
+ COLLECTOR_END_INSTR(PushBlockContext)
+
+ COLLECTOR_BEGIN_INSTR(CloneBlockContext)
+ COLLECTOR_END_INSTR(CloneBlockContext)
+
+ COLLECTOR_BEGIN_INSTR(PushScriptContext)
+ COLLECTOR_END_INSTR(PushScriptContext)
+
+ COLLECTOR_BEGIN_INSTR(PopScriptContext)
+ COLLECTOR_END_INSTR(PopScriptContext)
+
+ COLLECTOR_BEGIN_INSTR(PopContext)
+ COLLECTOR_END_INSTR(PopContext)
+
+ COLLECTOR_BEGIN_INSTR(GetIterator)
+ COLLECTOR_END_INSTR(GetIterator)
+
+ COLLECTOR_BEGIN_INSTR(IteratorNext)
+ COLLECTOR_END_INSTR(IteratorNext)
+
+ COLLECTOR_BEGIN_INSTR(IteratorClose)
+ COLLECTOR_END_INSTR(IteratorClose)
+
+ COLLECTOR_BEGIN_INSTR(DestructureRestElement)
+ COLLECTOR_END_INSTR(DestructureRestElement)
+
+ COLLECTOR_BEGIN_INSTR(DeleteProperty)
+ COLLECTOR_END_INSTR(DeleteProperty)
+
+ COLLECTOR_BEGIN_INSTR(DeleteName)
+ COLLECTOR_END_INSTR(DeleteName)
+
+ COLLECTOR_BEGIN_INSTR(TypeofName)
+ COLLECTOR_END_INSTR(TypeofName)
+
+ COLLECTOR_BEGIN_INSTR(TypeofValue)
+ COLLECTOR_END_INSTR(TypeofValue)
+
+ COLLECTOR_BEGIN_INSTR(DeclareVar)
+ COLLECTOR_END_INSTR(DeclareVar)
+
+ COLLECTOR_BEGIN_INSTR(DefineArray)
+ COLLECTOR_END_INSTR(DefineArray)
+
+ COLLECTOR_BEGIN_INSTR(DefineObjectLiteral)
+ COLLECTOR_END_INSTR(DefineObjectLiteral)
+
+ COLLECTOR_BEGIN_INSTR(CreateClass)
+ COLLECTOR_END_INSTR(CreateClass)
+
+ COLLECTOR_BEGIN_INSTR(CreateMappedArgumentsObject)
+ COLLECTOR_END_INSTR(CreateMappedArgumentsObject)
+
+ COLLECTOR_BEGIN_INSTR(CreateUnmappedArgumentsObject)
+ COLLECTOR_END_INSTR(CreateUnmappedArgumentsObject)
+
+ COLLECTOR_BEGIN_INSTR(CreateRestParameter)
+ COLLECTOR_END_INSTR(CreateRestParameter)
+
+ COLLECTOR_BEGIN_INSTR(ConvertThisToObject)
+ COLLECTOR_END_INSTR(ConvertThisToObject)
+
+ COLLECTOR_BEGIN_INSTR(LoadSuperConstructor)
+ COLLECTOR_END_INSTR(LoadSuperConstructor)
+
+ COLLECTOR_BEGIN_INSTR(ToObject)
+ COLLECTOR_END_INSTR(ToObject)
+
+ COLLECTOR_BEGIN_INSTR(Jump)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(Jump)
+
+ COLLECTOR_BEGIN_INSTR(JumpTrue)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpTrue)
+
+ COLLECTOR_BEGIN_INSTR(JumpFalse)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpFalse)
+
+ COLLECTOR_BEGIN_INSTR(JumpNoException)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNoException)
+
+ COLLECTOR_BEGIN_INSTR(JumpNotUndefined)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNotUndefined)
+
+ COLLECTOR_BEGIN_INSTR(CmpEqNull)
+ COLLECTOR_END_INSTR(CmpEqNull)
+
+ COLLECTOR_BEGIN_INSTR(CmpNeNull)
+ COLLECTOR_END_INSTR(CmpNeNull)
+
+ COLLECTOR_BEGIN_INSTR(CmpEqInt)
+ COLLECTOR_END_INSTR(CmpEq)
+
+ COLLECTOR_BEGIN_INSTR(CmpNeInt)
+ COLLECTOR_END_INSTR(CmpNeInt)
+
+ COLLECTOR_BEGIN_INSTR(CmpEq)
+ COLLECTOR_END_INSTR(CmpEq)
+
+ COLLECTOR_BEGIN_INSTR(CmpNe)
+ COLLECTOR_END_INSTR(CmpNe)
+
+ COLLECTOR_BEGIN_INSTR(CmpGt)
+ COLLECTOR_END_INSTR(CmpGt)
+
+ COLLECTOR_BEGIN_INSTR(CmpGe)
+ COLLECTOR_END_INSTR(CmpGe)
+
+ COLLECTOR_BEGIN_INSTR(CmpLt)
+ COLLECTOR_END_INSTR(CmpLt)
+
+ COLLECTOR_BEGIN_INSTR(CmpLe)
+ COLLECTOR_END_INSTR(CmpLe)
+
+ COLLECTOR_BEGIN_INSTR(CmpStrictEqual)
+ COLLECTOR_END_INSTR(CmpStrictEqual)
+
+ COLLECTOR_BEGIN_INSTR(CmpStrictNotEqual)
+ COLLECTOR_END_INSTR(CmpStrictNotEqual)
+
+ COLLECTOR_BEGIN_INSTR(CmpIn)
+ COLLECTOR_END_INSTR(CmpIn)
+
+ COLLECTOR_BEGIN_INSTR(CmpInstanceOf)
+ COLLECTOR_END_INSTR(CmpInstanceOf)
+
+ COLLECTOR_BEGIN_INSTR(UNot)
+ COLLECTOR_END_INSTR(UNot)
+
+ COLLECTOR_BEGIN_INSTR(UPlus)
+ COLLECTOR_END_INSTR(UPlus)
+
+ COLLECTOR_BEGIN_INSTR(UMinus)
+ COLLECTOR_END_INSTR(UMinus)
+
+ COLLECTOR_BEGIN_INSTR(UCompl)
+ COLLECTOR_END_INSTR(UCompl)
+
+ COLLECTOR_BEGIN_INSTR(Increment)
+ COLLECTOR_END_INSTR(PreIncrement)
+
+ COLLECTOR_BEGIN_INSTR(Decrement)
+ COLLECTOR_END_INSTR(PreDecrement)
+
+ COLLECTOR_BEGIN_INSTR(Add)
+ COLLECTOR_END_INSTR(Add)
+
+ COLLECTOR_BEGIN_INSTR(BitAnd)
+ COLLECTOR_END_INSTR(BitAnd)
+
+ COLLECTOR_BEGIN_INSTR(BitOr)
+ COLLECTOR_END_INSTR(BitOr)
+
+ COLLECTOR_BEGIN_INSTR(BitXor)
+ COLLECTOR_END_INSTR(BitXor)
+
+ COLLECTOR_BEGIN_INSTR(UShr)
+ COLLECTOR_END_INSTR(UShr)
+
+ COLLECTOR_BEGIN_INSTR(Shr)
+ COLLECTOR_END_INSTR(Shr)
+
+ COLLECTOR_BEGIN_INSTR(Shl)
+ COLLECTOR_END_INSTR(Shl)
+
+ COLLECTOR_BEGIN_INSTR(BitAndConst)
+ COLLECTOR_END_INSTR(BitAndConst)
+
+ COLLECTOR_BEGIN_INSTR(BitOrConst)
+ COLLECTOR_END_INSTR(BitOr)
+
+ COLLECTOR_BEGIN_INSTR(BitXorConst)
+ COLLECTOR_END_INSTR(BitXor)
+
+ COLLECTOR_BEGIN_INSTR(UShrConst)
+ COLLECTOR_END_INSTR(UShrConst)
+
+ COLLECTOR_BEGIN_INSTR(ShrConst)
+ COLLECTOR_END_INSTR(ShrConst)
+
+ COLLECTOR_BEGIN_INSTR(ShlConst)
+ COLLECTOR_END_INSTR(ShlConst)
+
+ COLLECTOR_BEGIN_INSTR(Exp)
+ COLLECTOR_END_INSTR(Exp)
+
+ COLLECTOR_BEGIN_INSTR(Mul)
+ COLLECTOR_END_INSTR(Mul)
+
+ COLLECTOR_BEGIN_INSTR(Div)
+ COLLECTOR_END_INSTR(Div)
+
+ COLLECTOR_BEGIN_INSTR(Mod)
+ COLLECTOR_END_INSTR(Mod)
+
+ COLLECTOR_BEGIN_INSTR(Sub)
+ COLLECTOR_END_INSTR(Sub)
+
+ COLLECTOR_BEGIN_INSTR(Ret)
+ COLLECTOR_END_INSTR(Ret)
+
+#ifndef QT_NO_QML_DEBUGGER
+ COLLECTOR_BEGIN_INSTR(Debug)
+ COLLECTOR_END_INSTR(Debug)
+#endif // QT_NO_QML_DEBUGGER
+
+ COLLECTOR_BEGIN_INSTR(LoadQmlContext)
+ COLLECTOR_END_INSTR(LoadQmlContext)
+
+ COLLECTOR_BEGIN_INSTR(LoadQmlImportedScripts)
+ COLLECTOR_END_INSTR(LoadQmlImportedScripts)
+ }
+
+ return labels;
+}
+
+#undef COLLECTOR_BEGIN_INSTR
+#undef COLLECTOR_END_INSTR
diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h
new file mode 100644
index 0000000000..5f1121306f
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4BYTECODEHANDLER_P_H
+#define QV4BYTECODEHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+#include <private/qv4instr_moth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace Moth {
+
+#define BYTECODE_HANDLER_DEFINE_ARGS(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(BYTECODE_HANDLER_DEFINE_ARGS##nargs(__VA_ARGS__))
+
+#define BYTECODE_HANDLER_DEFINE_ARGS0()
+#define BYTECODE_HANDLER_DEFINE_ARGS1(arg) \
+ int arg
+#define BYTECODE_HANDLER_DEFINE_ARGS2(arg1, arg2) \
+ int arg1, \
+ int arg2
+#define BYTECODE_HANDLER_DEFINE_ARGS3(arg1, arg2, arg3) \
+ int arg1, \
+ int arg2, \
+ int arg3
+#define BYTECODE_HANDLER_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
+ int arg1, \
+ int arg2, \
+ int arg3, \
+ int arg4
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \
+ virtual void generate_##name( \
+ BYTECODE_HANDLER_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ ) = 0;
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
+ INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+class ByteCodeHandler
+{
+public:
+ virtual ~ByteCodeHandler();
+
+ void decode(const char *code, uint len);
+
+ int instructionOffset() const { return _offset; }
+
+ static std::vector<int> collectLabelsInBytecode(const char *code, uint len);
+
+protected:
+ FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+ virtual void startInstruction(Moth::Instr::Type instr) = 0;
+ virtual void endInstruction(Moth::Instr::Type instr) = 0;
+
+private:
+ int _offset = 0;
+};
+
+} // Moth namespace
+} // QV4 namespace
+
+QT_END_NAMESPACE
+
+#endif // QV4BYTECODEHANDLER_P_H
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index e831fd48f5..4b766accbd 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -58,6 +58,8 @@
#include <cmath>
#include <iostream>
+static const bool disable_lookups = false;
+
#ifdef CONST
#undef CONST
#endif
@@ -77,8 +79,6 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
case Statement::Kind_ForEachStatement:
case Statement::Kind_ForStatement:
case Statement::Kind_IfStatement:
- case Statement::Kind_LocalForEachStatement:
- case Statement::Kind_LocalForStatement:
case Statement::Kind_WhileStatement:
bytecodeGenerator->setLocation(fallback);
break;
@@ -90,7 +90,7 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
: _module(nullptr)
- , _returnAddress(0)
+ , _returnAddress(-1)
, _context(nullptr)
, _labelledStatement(nullptr)
, jsUnitGenerator(jsUnitGenerator)
@@ -106,7 +106,7 @@ void Codegen::generateFromProgram(const QString &fileName,
const QString &sourceCode,
Program *node,
Module *module,
- CompilationMode mode)
+ ContextType contextType)
{
Q_ASSERT(node);
@@ -117,10 +117,13 @@ void Codegen::generateFromProgram(const QString &fileName,
_module->fileName = fileName;
_module->finalUrl = finalUrl;
- ScanFunctions scan(this, sourceCode, mode);
+ ScanFunctions scan(this, sourceCode, contextType);
scan(node);
- defineFunction(QStringLiteral("%entry"), node, nullptr, node->elements);
+ if (hasError)
+ return;
+
+ defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements);
}
void Codegen::enterContext(Node *node)
@@ -132,19 +135,24 @@ void Codegen::enterContext(Node *node)
int Codegen::leaveContext()
{
Q_ASSERT(_context);
- Q_ASSERT(!_context->controlFlow);
int functionIndex = _context->functionIndex;
_context = _context->parent;
return functionIndex;
}
+Context *Codegen::enterBlock(Node *node)
+{
+ enterContext(node);
+ return _context;
+}
+
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
return _expr.result();
#ifndef V4_BOOTSTRAP
- if (expr.isConst()) {
+ if (expr.isConstant()) {
auto v = Value::fromReturnedValue(expr.constant);
if (v.isNumber()) {
switch (op) {
@@ -267,9 +275,9 @@ void Codegen::statement(Statement *ast)
bytecodeGenerator->setLocation(ast->firstSourceLocation());
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
}
void Codegen::statement(ExpressionNode *ast)
@@ -282,11 +290,11 @@ void Codegen::statement(ExpressionNode *ast)
Result r(nx);
qSwap(_expr, r);
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
qSwap(_expr, r);
if (hasError)
@@ -337,60 +345,103 @@ Codegen::Reference Codegen::expression(ExpressionNode *ast)
return r.result();
}
-Codegen::Result Codegen::sourceElement(SourceElement *ast)
+void Codegen::program(Program *ast)
{
- Result r(nx);
if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
+ statementList(ast->statements);
}
- return r;
}
-void Codegen::functionBody(FunctionBody *ast)
-{
- if (ast)
- sourceElements(ast->elements);
-}
+enum class CompletionState {
+ Empty,
+ EmptyAbrupt,
+ NonEmpty
+};
-void Codegen::program(Program *ast)
-{
- if (ast) {
- sourceElements(ast->elements);
+static CompletionState completionState(StatementList *list)
+{
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return CompletionState::EmptyAbrupt;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableDeclaration ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState subState = completionState(static_cast<Block *>(it->statement)->statements);
+ if (subState != CompletionState::Empty)
+ return subState;
+ continue;
+ }
+ return CompletionState::NonEmpty;
}
+ return CompletionState::Empty;
}
-void Codegen::sourceElements(SourceElements *ast)
+static Node *completionStatement(StatementList *list)
{
- bool _requiresReturnValue = false;
- qSwap(_requiresReturnValue, requiresReturnValue);
- for (SourceElements *it = ast; it; it = it->next) {
- if (!it->next)
- qSwap(_requiresReturnValue, requiresReturnValue);
- sourceElement(it->element);
- if (hasError)
- return;
- if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(it->element)) {
- if (AST::cast<ThrowStatement *>(sse->statement) ||
- AST::cast<ReturnStatement *>(sse->statement))
- return;
+ Node *completionStatement = nullptr;
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return completionStatement;
+ if (it->statement->kind == Statement::Kind_ThrowStatement ||
+ it->statement->kind == Statement::Kind_ReturnStatement)
+ return it->statement;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableStatement ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState state = completionState(static_cast<Block *>(it->statement)->statements);
+ switch (state) {
+ case CompletionState::Empty:
+ continue;
+ case CompletionState::EmptyAbrupt:
+ return it->statement;
+ case CompletionState::NonEmpty:
+ break;
+ }
}
+ completionStatement = it->statement;
}
+ return completionStatement;
}
void Codegen::statementList(StatementList *ast)
{
+ if (!ast)
+ return;
+
bool _requiresReturnValue = requiresReturnValue;
- requiresReturnValue = false;
- for (StatementList *it = ast; it; it = it->next) {
- if (!it->next ||
- it->next->statement->kind == Statement::Kind_BreakStatement ||
- it->next->statement->kind == Statement::Kind_ContinueStatement ||
- it->next->statement->kind == Statement::Kind_ReturnStatement)
- requiresReturnValue = _requiresReturnValue;
- statement(it->statement);
+ // ### the next line is pessimizing a bit too much, as there are many cases, where the complietion from the break
+ // statement will not be used, but it's at least spec compliant
+ if (!controlFlow || !controlFlow->hasLoop())
requiresReturnValue = false;
+
+ Node *needsCompletion = nullptr;
+
+ if (_requiresReturnValue && !requiresReturnValue)
+ needsCompletion = completionStatement(ast);
+
+ if (requiresReturnValue && !needsCompletion && !insideSwitch) {
+ // break or continue is the first real statement, set the return value to undefined
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+ }
+
+ bool _insideSwitch = insideSwitch;
+ insideSwitch = false;
+
+ for (StatementList *it = ast; it; it = it->next) {
+ if (it->statement == needsCompletion)
+ requiresReturnValue = true;
+ if (Statement *s = it->statement->statementCast())
+ statement(s);
+ else
+ statement(static_cast<ExpressionNode *>(it->statement));
+ if (it->statement == needsCompletion)
+ requiresReturnValue = false;
if (it->statement->kind == Statement::Kind_ThrowStatement ||
it->statement->kind == Statement::Kind_BreakStatement ||
it->statement->kind == Statement::Kind_ContinueStatement ||
@@ -399,22 +450,16 @@ void Codegen::statementList(StatementList *ast)
break;
}
requiresReturnValue = _requiresReturnValue;
+ insideSwitch = _insideSwitch;
}
-void Codegen::variableDeclaration(VariableDeclaration *ast)
+void Codegen::variableDeclaration(PatternElement *ast)
{
RegisterScope scope(this);
- if (!ast->expression)
+ if (!ast->initializer)
return;
- Reference rhs = expression(ast->expression);
- if (hasError)
- return;
-
- Reference lhs = referenceForName(ast->name.toString(), true);
- //### if lhs is a temp, this won't generate a temp-to-temp move. Same for when rhs is a const
- rhs.loadInAccumulator();
- lhs.storeConsumeAccumulator();
+ initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true);
}
void Codegen::variableDeclarationList(VariableDeclarationList *ast)
@@ -424,6 +469,200 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast)
}
}
+Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p)
+{
+ if (!p->bindingIdentifier.isNull())
+ return referenceForName(p->bindingIdentifier.toString(), true);
+ if (!p->bindingTarget || p->destructuringPattern())
+ return Codegen::Reference::fromStackSlot(this);
+ Reference lhs = expression(p->bindingTarget);
+ if (hasError)
+ return lhs;
+ lhs = lhs.asLValue();
+ return lhs;
+}
+
+void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base, bool isDefinition)
+{
+ Q_ASSERT(e->type == AST::PatternElement::Binding || e->type == AST::PatternElement::RestElement);
+ RegisterScope scope(this);
+ Reference baseRef = (base.isAccumulator()) ? base.storeOnStack() : base;
+ Reference varToStore = targetForPatternElement(e);
+ if (isDefinition)
+ varToStore.isReferenceToConst = false;
+ if (hasError)
+ return;
+
+ if (e->initializer) {
+ if (!baseRef.isValid()) {
+ // assignment
+ Reference expr = expression(e->initializer);
+ if (hasError)
+ return;
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ } else if (baseRef == varToStore) {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ jump.link();
+ } else {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ jump.link();
+ varToStore.storeConsumeAccumulator();
+ }
+ } else if (baseRef != varToStore && baseRef.isValid()) {
+ baseRef.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ }
+ Pattern *p = e->destructuringPattern();
+ if (!p)
+ return;
+
+ if (!varToStore.isStackSlot())
+ varToStore = varToStore.storeOnStack();
+ if (PatternElementList *l = e->elementList()) {
+ destructureElementList(varToStore, l, isDefinition);
+ } else if (PatternPropertyList *p = e->propertyList()) {
+ destructurePropertyList(varToStore, p, isDefinition);
+ } else if (e->bindingTarget) {
+ // empty binding pattern. For spec compatibility, try to coerce the argument to an object
+ varToStore.loadInAccumulator();
+ Instruction::ToObject toObject;
+ bytecodeGenerator->addInstruction(toObject);
+ return;
+ }
+}
+
+Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name)
+{
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(name);
+ Reference property;
+ if (cname) {
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return Reference();
+ computedName = computedName.storeOnStack();
+ property = Reference::fromSubscript(object, computedName).asLValue();
+ } else {
+ QString propertyName = name->asString();
+ property = Reference::fromMember(object, propertyName);
+ }
+ return property;
+}
+
+void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList, bool isDefinition)
+{
+ RegisterScope scope(this);
+
+ for (PatternPropertyList *it = bindingList; it; it = it->next) {
+ PatternProperty *p = it->property;
+ RegisterScope scope(this);
+ Reference property = referenceForPropertyName(object, p->name);
+ if (hasError)
+ return;
+ initializeAndDestructureBindingElement(p, property, isDefinition);
+ if (hasError)
+ return;
+ }
+}
+
+void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList, bool isDefinition)
+{
+ RegisterScope scope(this);
+
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference iteratorValue = Reference::fromStackSlot(this);
+ Reference iteratorDone = Reference::fromStackSlot(this);
+
+ array.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = 1; // ForEachType::Of
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+
+ bool hadNext = false;
+ bool hasRest = false;
+
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+
+ for (PatternElementList *p = bindingList; p; p = p->next) {
+ PatternElement *e = p->element;
+ for (Elision *elision = p->elision; elision; elision = elision->next) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = iteratorValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ hadNext = true;
+ bool last = !elision->next && !e && !p->next;
+ if (last)
+ iteratorDone.storeConsumeAccumulator();
+ }
+
+ if (!e)
+ continue;
+
+ hadNext = true;
+ RegisterScope scope(this);
+ iterator.loadInAccumulator();
+
+ if (e->type == PatternElement::RestElement) {
+ bytecodeGenerator->addInstruction(Instruction::DestructureRestElement());
+ initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this), isDefinition);
+ hasRest = true;
+ } else {
+ Instruction::IteratorNext next;
+ next.value = iteratorValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bool last = !p->next || (!p->next->elision && !p->next->element);
+ if (last)
+ iteratorDone.storeConsumeAccumulator();
+ initializeAndDestructureBindingElement(e, iteratorValue, isDefinition);
+ if (hasError) {
+ end.link();
+ return;
+ }
+ }
+ }
+
+ if (!hadNext) {
+ Reference::storeConstOnStack(this, Encode(false), iteratorDone.stackSlot());
+ }
+
+ if (!hasRest) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ }
+
+ end.link();
+}
+
+void Codegen::destructurePattern(Pattern *p, const Reference &rhs)
+{
+ RegisterScope scope(this);
+ if (auto *o = AST::cast<ObjectPattern *>(p))
+ destructurePropertyList(rhs, o->properties);
+ else if (auto *a = AST::cast<ArrayPattern *>(p))
+ destructureElementList(rhs, a->elements);
+ else
+ Q_UNREACHABLE();
+}
+
bool Codegen::visit(ArgumentList *)
{
@@ -461,12 +700,6 @@ bool Codegen::visit(DefaultClause *)
return false;
}
-bool Codegen::visit(ElementList *)
-{
- Q_UNREACHABLE();
- return false;
-}
-
bool Codegen::visit(Elision *)
{
Q_UNREACHABLE();
@@ -485,37 +718,31 @@ bool Codegen::visit(FormalParameterList *)
return false;
}
-bool Codegen::visit(FunctionBody *)
-{
- Q_UNREACHABLE();
- return false;
-}
-
bool Codegen::visit(Program *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyAssignmentList *)
+bool Codegen::visit(PatternElement *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyNameAndValue *)
+bool Codegen::visit(PatternElementList *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyGetterSetter *)
+bool Codegen::visit(PatternProperty *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(SourceElements *)
+bool Codegen::visit(PatternPropertyList *)
{
Q_UNREACHABLE();
return false;
@@ -587,15 +814,121 @@ bool Codegen::visit(UiQualifiedPragmaId *)
return false;
}
-bool Codegen::visit(VariableDeclaration *)
+bool Codegen::visit(VariableDeclarationList *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(VariableDeclarationList *)
+bool Codegen::visit(ClassExpression *ast)
{
- Q_UNREACHABLE();
+ Compiler::Class jsClass;
+ jsClass.nameIndex = registerString(ast->name.toString());
+
+ ClassElementList *constructor = nullptr;
+ int nComputedNames = 0;
+ int nStaticComputedNames = 0;
+
+ RegisterScope scope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ PatternProperty *p = member->property;
+ FunctionExpression *f = p->initializer->asFunctionDefinition();
+ Q_ASSERT(f);
+ AST::ComputedPropertyName *cname = AST::cast<ComputedPropertyName *>(p->name);
+ if (cname) {
+ ++nComputedNames;
+ if (member->isStatic)
+ ++nStaticComputedNames;
+ }
+ QString name = p->name->asString();
+ uint nameIndex = cname ? UINT_MAX : registerString(name);
+ Compiler::Class::Method::Type type = Compiler::Class::Method::Regular;
+ if (p->type == PatternProperty::Getter)
+ type = Compiler::Class::Method::Getter;
+ else if (p->type == PatternProperty::Setter)
+ type = Compiler::Class::Method::Setter;
+ Compiler::Class::Method m{ nameIndex, type, static_cast<uint>(defineFunction(name, f, f->formals, f->body)) };
+
+ if (member->isStatic) {
+ if (name == QStringLiteral("prototype")) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a static method named 'prototype'."));
+ return false;
+ }
+ jsClass.staticMethods << m;
+ } else {
+ if (name == QStringLiteral("constructor")) {
+ if (constructor) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a multiple constructors in a class."));
+ return false;
+ }
+ if (m.type != Compiler::Class::Method::Regular) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a getter or setter named 'constructor'."));
+ return false;
+ }
+ constructor = member;
+ jsClass.constructorIndex = m.functionIndex;
+ continue;
+ }
+
+ jsClass.methods << m;
+ }
+ }
+
+ int classIndex = _module->classes.size();
+ _module->classes.append(jsClass);
+
+ Reference heritage = Reference::fromStackSlot(this);
+ if (ast->heritage) {
+ bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation());
+ Reference r = expression(ast->heritage);
+ if (hasError)
+ return false;
+ r.storeOnStack(heritage.stackSlot());
+ } else {
+ Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator();
+ heritage.storeConsumeAccumulator();
+ }
+
+ int computedNames = nComputedNames ? bytecodeGenerator->newRegisterArray(nComputedNames) : 0;
+ int currentStaticName = computedNames;
+ int currentNonStaticName = computedNames + nStaticComputedNames;
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(member->property->name);
+ if (!cname)
+ continue;
+ RegisterScope scope(this);
+ bytecodeGenerator->setLocation(cname->firstSourceLocation());
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return false;
+ computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++);
+ }
+
+ Instruction::CreateClass createClass;
+ createClass.classIndex = classIndex;
+ createClass.heritage = heritage.stackSlot();
+ createClass.computedNames = computedNames;
+
+ bytecodeGenerator->addInstruction(createClass);
+
+ if (!ast->name.isEmpty()) {
+ Reference ctor = referenceForName(ast->name.toString(), true);
+ ctor.isReferenceToConst = false; // this is the definition
+ (void) ctor.storeRetainAccumulator();
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+}
+
+bool Codegen::visit(ClassDeclaration *ast)
+{
+ Reference outerVar = referenceForName(ast->name.toString(), true);
+ visit(static_cast<ClassExpression *>(ast));
+ (void) outerVar.storeRetainAccumulator();
return false;
}
@@ -609,50 +942,160 @@ bool Codegen::visit(Expression *ast)
return false;
}
-bool Codegen::visit(ArrayLiteral *ast)
+bool Codegen::visit(ArrayPattern *ast)
{
if (hasError)
return false;
- RegisterScope scope(this);
+ PatternElementList *it = ast->elements;
int argc = 0;
- int args = -1;
- auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
- int temp = bytecodeGenerator->newRegister();
- if (args == -1)
- args = temp;
- if (!arg) {
- auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue());
- (void) c.storeOnStack(temp);
- } else {
- RegisterScope scope(this);
- (void) expression(arg).storeOnStack(temp);
+ {
+ RegisterScope scope(this);
+
+ int args = -1;
+ auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ if (!arg) {
+ auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue());
+ (void) c.storeOnStack(temp);
+ } else {
+ RegisterScope scope(this);
+ Reference r = expression(arg);
+ if (hasError)
+ return;
+ (void) r.storeOnStack(temp);
+ }
+ ++argc;
+ };
+
+ for (; it; it = it->next) {
+ PatternElement *e = it->element;
+ if (e && e->type == PatternElement::SpreadElement)
+ break;
+ for (Elision *elision = it->elision; elision; elision = elision->next)
+ push(nullptr);
+
+ if (!e)
+ continue;
+
+ push(e->initializer);
+ if (hasError)
+ return false;
}
- ++argc;
- };
- for (ElementList *it = ast->elements; it; it = it->next) {
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
+ }
- for (Elision *elision = it->elision; elision; elision = elision->next)
- push(nullptr);
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+ }
- push(it->expression);
- if (hasError)
- return false;
+ if (!it) {
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
}
- for (Elision *elision = ast->elision; elision; elision = elision->next)
- push(nullptr);
+ Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
- if (args == -1) {
- Q_ASSERT(argc == 0);
- args = 0;
+ RegisterScope scope(this);
+ Reference array = Reference::fromStackSlot(this);
+ array.storeConsumeAccumulator();
+ Reference index = Reference::storeConstOnStack(this, Encode(argc));
+
+ auto pushAccumulator = [&]() {
+ Reference slot = Reference::fromSubscript(array, index);
+ slot.storeConsumeAccumulator();
+
+ index.loadInAccumulator();
+ Instruction::Increment inc;
+ bytecodeGenerator->addInstruction(inc);
+ index.storeConsumeAccumulator();
+ };
+
+ while (it) {
+ for (Elision *elision = it->elision; elision; elision = elision->next) {
+ Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).loadInAccumulator();
+ pushAccumulator();
+ }
+
+ if (!it->element) {
+ it = it->next;
+ continue;
+ }
+
+ // handle spread element
+ if (it->element->type == PatternElement::SpreadElement) {
+ RegisterScope scope(this);
+
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference lhsValue = Reference::fromStackSlot(this);
+
+ // There should be a temporal block, so that variables declared in lhs shadow outside vars.
+ // This block should define a temporal dead zone for those variables, which is not yet implemented.
+ {
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = /*ForEachType::Of*/ 1;
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
+
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
+
+ {
+ ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true);
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ lhsValue.loadInAccumulator();
+ pushAccumulator();
+
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+ }
+
+ end.link();
+
+ Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+
+ done.link();
+ } else {
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ pushAccumulator();
+ }
+
+ it = it->next;
}
- Instruction::DefineArray call;
- call.argc = argc;
- call.args = Moth::StackSlot::createRegister(args);
- bytecodeGenerator->addInstruction(call);
+ array.loadInAccumulator();
_expr.setResult(Reference::fromAccumulator(this));
return false;
@@ -666,7 +1109,14 @@ bool Codegen::visit(ArrayMemberExpression *ast)
Reference base = expression(ast->base);
if (hasError)
return false;
+ if (base.isSuper()) {
+ Reference index = expression(ast->expression).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(index));
+ return false;
+ }
base = base.storeOnStack();
+ if (hasError)
+ return false;
if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) {
QString s = str->value.toString();
uint arrayIndex = QV4::String::toArrayIndex(s);
@@ -692,6 +1142,7 @@ static QSOperator::Op baseOp(int op)
case QSOperator::InplaceAdd: return QSOperator::Add;
case QSOperator::InplaceLeftShift: return QSOperator::LShift;
case QSOperator::InplaceMod: return QSOperator::Mod;
+ case QSOperator::InplaceExp: return QSOperator::Exp;
case QSOperator::InplaceMul: return QSOperator::Mul;
case QSOperator::InplaceOr: return QSOperator::BitOr;
case QSOperator::InplaceRightShift: return QSOperator::RShift;
@@ -764,19 +1215,21 @@ bool Codegen::visit(BinaryExpression *ast)
_expr.setResult(Reference::fromAccumulator(this));
}
return false;
- }
-
- Reference left = expression(ast->left);
- if (hasError)
- return false;
-
- switch (ast->op) {
- case QSOperator::Or:
- case QSOperator::And:
- Q_UNREACHABLE(); // handled separately above
- break;
+ } else if (ast->op == QSOperator::Assign) {
+ if (AST::Pattern *p = ast->left->patternCast()) {
+ RegisterScope scope(this);
+ Reference right = expression(ast->right).storeOnStack();
+ destructurePattern(p, right);
+ if (!_expr.accept(nx)) {
+ right.loadInAccumulator();
+ _expr.setResult(Reference::fromAccumulator(this));
+ }
+ return false;
+ }
+ Reference left = expression(ast->left);
+ if (hasError)
+ return false;
- case QSOperator::Assign: {
if (!left.isLValue()) {
throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
return false;
@@ -792,15 +1245,27 @@ bool Codegen::visit(BinaryExpression *ast)
_expr.setResult(left.storeConsumeAccumulator());
else
_expr.setResult(left.storeRetainAccumulator());
- break;
+ return false;
}
+ Reference left = expression(ast->left);
+ if (hasError)
+ return false;
+
+ switch (ast->op) {
+ case QSOperator::Or:
+ case QSOperator::And:
+ case QSOperator::Assign:
+ Q_UNREACHABLE(); // handled separately above
+ break;
+
case QSOperator::InplaceAnd:
case QSOperator::InplaceSub:
case QSOperator::InplaceDiv:
case QSOperator::InplaceAdd:
case QSOperator::InplaceLeftShift:
case QSOperator::InplaceMod:
+ case QSOperator::InplaceExp:
case QSOperator::InplaceMul:
case QSOperator::InplaceOr:
case QSOperator::InplaceRightShift:
@@ -830,7 +1295,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::BitAnd:
case QSOperator::BitOr:
case QSOperator::BitXor:
- if (left.isConst()) {
+ if (left.isConstant()) {
Reference right = expression(ast->right);
if (hasError)
return false;
@@ -850,6 +1315,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::StrictNotEqual:
case QSOperator::Add:
case QSOperator::Div:
+ case QSOperator::Exp:
case QSOperator::Mod:
case QSOperator::Mul:
case QSOperator::Sub:
@@ -890,7 +1356,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Sub: {
- if (right.isConst() && right.constant == Encode(int(1))) {
+ if (right.isConstant() && right.constant == Encode(int(1))) {
left.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::Decrement());
} else {
@@ -902,6 +1368,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
}
+ case QSOperator::Exp: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Exp exp;
+ exp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(exp);
+ break;
+ }
case QSOperator::Mul: {
left = left.storeOnStack();
right.loadInAccumulator();
@@ -927,9 +1401,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::BitAnd:
- if (right.isConst()) {
+ if (right.isConstant()) {
int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
+ if (left.isConstant()) {
int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt;
return Reference::fromConst(this, Encode(result));
}
@@ -945,9 +1419,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::BitOr:
- if (right.isConst()) {
+ if (right.isConstant()) {
int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
+ if (left.isConstant()) {
int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt;
return Reference::fromConst(this, Encode(result));
}
@@ -963,9 +1437,9 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::BitXor:
- if (right.isConst()) {
+ if (right.isConstant()) {
int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
+ if (left.isConstant()) {
int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
return Reference::fromConst(this, Encode(result));
}
@@ -981,7 +1455,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::URShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::UShrConst ushr;
ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
@@ -994,7 +1468,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::RShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShrConst shr;
shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
@@ -1007,7 +1481,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::LShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShlConst shl;
shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
@@ -1146,12 +1620,12 @@ static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper)
Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
{
- if (left.isConst()) {
+ if (left.isConstant()) {
oper = operatorForSwappedOperands(oper);
qSwap(left, right);
}
- if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
+ if (right.isConstant() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
Value c = Primitive::fromReturnedValue(right.constant);
if (c.isNull() || c.isUndefined()) {
left.loadInAccumulator();
@@ -1259,6 +1733,7 @@ bool Codegen::visit(CallExpression *ast)
RegisterScope scope(this);
Reference base = expression(ast->base);
+
if (hasError)
return false;
switch (base.type) {
@@ -1270,15 +1745,50 @@ bool Codegen::visit(CallExpression *ast)
break;
case Reference::Name:
break;
+ case Reference::Super:
+ handleConstruct(base, ast->arguments);
+ return false;
default:
base = base.storeOnStack();
break;
}
+ int thisObject = bytecodeGenerator->newRegister();
+ int functionObject = bytecodeGenerator->newRegister();
+
auto calldata = pushArgs(ast->arguments);
if (hasError)
return false;
+ if (calldata.hasSpread) {
+ Reference baseObject = base.baseObject();
+ if (!baseObject.isStackSlot()) {
+ baseObject.storeOnStack(thisObject);
+ baseObject = Reference::fromStackSlot(this, thisObject);
+ }
+ if (!base.isStackSlot()) {
+ base.storeOnStack(functionObject);
+ base = Reference::fromStackSlot(this, functionObject);
+ }
+
+ Instruction::CallWithSpread call;
+ call.func = base.stackSlot();
+ call.thisObject = baseObject.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+
+ }
+
+ handleCall(base, calldata);
+ return false;
+}
+
+void Codegen::handleCall(Reference &base, Arguments calldata)
+{
//### 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;
@@ -1295,7 +1805,7 @@ bool Codegen::visit(CallExpression *ast)
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
} else if (base.type == Reference::Member) {
- if (useFastLookups) {
+ if (!disable_lookups && useFastLookups) {
Instruction::CallPropertyLookup call;
call.base = base.propertyBase.stackSlot();
call.lookupIndex = registerGetterLookup(base.propertyNameIndex);
@@ -1323,7 +1833,7 @@ bool Codegen::visit(CallExpression *ast)
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
- } else if (useFastLookups && base.global) {
+ } else if (!disable_lookups && useFastLookups && base.global) {
Instruction::CallGlobalLookup call;
call.index = registerGlobalGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
@@ -1346,36 +1856,69 @@ bool Codegen::visit(CallExpression *ast)
}
_expr.setResult(Reference::fromAccumulator(this));
- return false;
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
{
+ bool hasSpread = false;
int argc = 0;
- for (ArgumentList *it = args; it; it = it->next)
+ for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ hasSpread = true;
+ ++argc;
+ }
++argc;
+ }
if (!argc)
- return { 0, 0 };
+ return { 0, 0, false };
int calldata = bytecodeGenerator->newRegisterArray(argc);
argc = 0;
for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ Reference::fromConst(this, Primitive::emptyValue().asReturnedValue()).storeOnStack(calldata + argc);
+ ++argc;
+ }
RegisterScope scope(this);
Reference e = expression(it->expression);
if (hasError)
break;
- if (!argc && !it->next) {
+ if (!argc && !it->next && !hasSpread) {
// avoid copy for functions taking a single argument
if (e.isStackSlot())
- return { 1, e.stackSlot() };
+ return { 1, e.stackSlot(), hasSpread };
}
(void) e.storeOnStack(calldata + argc);
++argc;
}
- return { argc, calldata };
+ return { argc, calldata, hasSpread };
+}
+
+Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args)
+{
+ int argc = 0;
+ for (TemplateLiteral *it = args; it; it = it->next)
+ ++argc;
+
+ if (!argc)
+ return { 0, 0, false };
+
+ int calldata = bytecodeGenerator->newRegisterArray(argc);
+
+ argc = 0;
+ for (TemplateLiteral *it = args; it && it->expression; it = it->next) {
+ RegisterScope scope(this);
+ Reference e = expression(it->expression);
+ if (hasError)
+ break;
+ (void) e.storeOnStack(calldata + argc);
+ ++argc;
+ }
+
+ return { argc, calldata, false };
}
bool Codegen::visit(ConditionalExpression *ast)
@@ -1413,11 +1956,15 @@ bool Codegen::visit(DeleteExpression *ast)
if (hasError)
return false;
+ RegisterScope scope(this);
Reference expr = expression(ast->expression);
if (hasError)
return false;
switch (expr.type) {
+ case Reference::SuperProperty:
+ // ### this should throw a reference error at runtime.
+ return false;
case Reference::StackSlot:
if (!expr.stackSlotIsLocalOrArgument)
break;
@@ -1444,9 +1991,14 @@ bool Codegen::visit(DeleteExpression *ast)
case Reference::Member: {
//### maybe add a variant where the base can be in the accumulator?
expr = expr.asLValue();
- Instruction::DeleteMember del;
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = expr.propertyNameIndex;
+ bytecodeGenerator->addInstruction(instr);
+ Reference index = Reference::fromStackSlot(this);
+ index.storeConsumeAccumulator();
+ Instruction::DeleteProperty del;
del.base = expr.propertyBase.stackSlot();
- del.member = expr.propertyNameIndex;
+ del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
_expr.setResult(Reference::fromAccumulator(this));
return false;
@@ -1454,7 +2006,7 @@ bool Codegen::visit(DeleteExpression *ast)
case Reference::Subscript: {
//### maybe add a variant where the index can be in the accumulator?
expr = expr.asLValue();
- Instruction::DeleteSubscript del;
+ Instruction::DeleteProperty del;
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
@@ -1478,109 +2030,169 @@ bool Codegen::visit(FalseLiteral *)
return false;
}
+bool Codegen::visit(SuperLiteral *)
+{
+ if (hasError)
+ return false;
+
+ _expr.setResult(Reference::fromSuper(this));
+ return false;
+}
+
bool Codegen::visit(FieldMemberExpression *ast)
{
if (hasError)
return false;
+ if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("new")) {
+ // new.target
+ if (ast->name != QLatin1String("target")) {
+ throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'."));
+ return false;
+ }
+ Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
+ _expr.setResult(r);
+ return false;
+ }
+ }
+
Reference base = expression(ast->base);
if (hasError)
return false;
+ if (base.isSuper()) {
+ Instruction::LoadRuntimeString load;
+ load.stringId = registerString(ast->name.toString());
+ bytecodeGenerator->addInstruction(load);
+ Reference property = Reference::fromAccumulator(this).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(property));
+ return false;
+ }
_expr.setResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
-bool Codegen::visit(FunctionExpression *ast)
+bool Codegen::visit(TaggedTemplate *ast)
{
if (hasError)
return false;
RegisterScope scope(this);
- int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr);
- loadClosure(function);
- _expr.setResult(Reference::fromAccumulator(this));
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ switch (base.type) {
+ case Reference::Member:
+ case Reference::Subscript:
+ base = base.asLValue();
+ break;
+ case Reference::Name:
+ break;
+ default:
+ base = base.storeOnStack();
+ break;
+ }
+
+ int arrayTemp = createTemplateArray(ast->templateLiteral);
+ Q_UNUSED(arrayTemp);
+ auto calldata = pushTemplateArgs(ast->templateLiteral);
+ if (hasError)
+ return false;
+ ++calldata.argc;
+ Q_ASSERT(calldata.argv == arrayTemp + 1);
+ --calldata.argv;
+
+ handleCall(base, calldata);
+ return false;
+
+
return false;
}
-Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
+int Codegen::createTemplateArray(TemplateLiteral *t)
{
- int scope = 0;
- Context *c = _context;
-
- // skip the innermost context if it's simple (as the runtime won't
- // create a context for it
- if (c->canUseSimpleCall()) {
- Context::Member m = c->findMember(name);
- if (m.type != Context::UndefinedMember) {
- Q_ASSERT((!m.canEscape));
- Reference r = Reference::fromStackSlot(this, m.index, true /*isLocal*/);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
- r.isArgOrEval = true;
- if (isLhs && c->isStrict)
- // ### add correct source location
- throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- }
- return r;
- }
- const int argIdx = c->findArgument(name);
- if (argIdx != -1) {
- Q_ASSERT(!c->argumentsCanEscape && (c->usesArgumentsObject != Context::ArgumentsObjectUsed || c->isStrict));
- return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
- }
- c = c->parent;
- }
+ int arrayTemp = bytecodeGenerator->newRegister();
- while (c->parent) {
- if (c->forceLookupByName())
- goto loadByName;
+ RegisterScope scope(this);
- Context::Member m = c->findMember(name);
- if (m.type != Context::UndefinedMember) {
- Reference r = m.canEscape ? Reference::fromScopedLocal(this, m.index, scope)
- : Reference::fromStackSlot(this, m.index, true /*isLocal*/);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
- r.isArgOrEval = true;
- if (isLhs && c->isStrict)
- // ### add correct source location
- throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- }
- return r;
- }
- const int argIdx = c->findArgument(name);
- if (argIdx != -1) {
- if (c->argumentsCanEscape || c->usesArgumentsObject == Context::ArgumentsObjectUsed) {
- int idx = argIdx + c->locals.size();
- return Reference::fromScopedLocal(this, idx, scope);
- } else {
- Q_ASSERT(scope == 0);
- return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
- }
- }
+ int argc = 0;
+ int args = -1;
+ auto push = [this, &argc, &args](const QStringRef &arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(arg.toString());
+ bytecodeGenerator->addInstruction(instr);
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
- if (!c->isStrict && c->hasDirectEval)
- goto loadByName;
+ ++argc;
+ };
- ++scope;
- c = c->parent;
- }
+ for (TemplateLiteral *it = t; it; it = it->next)
+ push(it->value);
- {
- // This hook allows implementing QML lookup semantics
- Reference fallback = fallbackNameLookup(name);
- if (fallback.type != Reference::Invalid)
- return fallback;
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
}
- if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) {
- Reference r = Reference::fromName(this, name);
- r.global = true;
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+
+ Instruction::StoreReg store;
+ store.reg = arrayTemp;
+ bytecodeGenerator->addInstruction(store);
+
+ return arrayTemp;
+}
+
+bool Codegen::visit(FunctionExpression *ast)
+{
+ if (hasError)
+ return false;
+
+ RegisterScope scope(this);
+
+ int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
+ if (hasError)
+ return false;
+ loadClosure(function);
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+}
+
+Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
+{
+ Context::ResolvedName resolved = _context->resolveName(name);
+
+ if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack) {
+ if (resolved.isArgOrEval && isLhs)
+ // ### add correct source location
+ throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ Reference r = (resolved.type == Context::ResolvedName::Local) ?
+ Reference::fromScopedLocal(this, resolved.index, resolved.scope) :
+ Reference::fromStackSlot(this, resolved.index, true /*isLocal*/);
+ if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name))
+ r.isVolatile = true;
+ r.isArgOrEval = resolved.isArgOrEval;
+ r.isReferenceToConst = resolved.isConst;
return r;
}
- // global context or with. Lookup by name
- loadByName:
- return Reference::fromName(this, name);
+ // 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 = (resolved.type == Context::ResolvedName::Global);
+ return r;
}
void Codegen::loadClosure(int closureId)
@@ -1618,6 +2230,47 @@ bool Codegen::visit(NestedExpression *ast)
return false;
}
+void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
+{
+ Reference constructor;
+ if (base.isSuper()) {
+ Instruction::LoadSuperConstructor super;
+ bytecodeGenerator->addInstruction(super);
+ constructor = Reference::fromAccumulator(this).storeOnStack();
+ } else {
+ constructor = base.storeOnStack();
+ }
+
+ auto calldata = pushArgs(arguments);
+ if (hasError)
+ return;
+
+ if (base.isSuper()) {
+ Reference::fromStackSlot(this, CallData::Function).loadInAccumulator();
+ } else {
+ constructor.loadInAccumulator();
+ }
+
+ if (calldata.hasSpread) {
+ Instruction::ConstructWithSpread create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ } else {
+ Instruction::Construct create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ }
+ if (base.isSuper())
+ // set the result up as the thisObject
+ Reference::fromAccumulator(this).storeOnStack(CallData::This);
+
+ _expr.setResult(Reference::fromAccumulator(this));
+}
+
bool Codegen::visit(NewExpression *ast)
{
if (hasError)
@@ -1628,15 +2281,12 @@ bool Codegen::visit(NewExpression *ast)
Reference base = expression(ast->expression);
if (hasError)
return false;
- //### Maybe create a ConstructA that takes an accumulator?
- base = base.storeOnStack();
+ if (base.isSuper()) {
+ throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
+ return false;
+ }
- Instruction::Construct create;
- create.func = base.stackSlot();
- create.argc = 0;
- create.argv = 0;
- bytecodeGenerator->addInstruction(create);
- _expr.setResult(Reference::fromAccumulator(this));
+ handleConstruct(base, nullptr);
return false;
}
@@ -1650,18 +2300,12 @@ bool Codegen::visit(NewMemberExpression *ast)
Reference base = expression(ast->base);
if (hasError)
return false;
- base = base.storeOnStack();
-
- auto calldata = pushArgs(ast->arguments);
- if (hasError)
+ if (base.isSuper()) {
+ throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
return false;
+ }
- Instruction::Construct create;
- create.func = base.stackSlot();
- create.argc = calldata.argc;
- create.argv = calldata.argv;
- bytecodeGenerator->addInstruction(create);
- _expr.setResult(Reference::fromAccumulator(this));
+ handleConstruct(base, ast->arguments);
return false;
}
@@ -1696,133 +2340,105 @@ bool Codegen::visit(NumericLiteral *ast)
return false;
}
-bool Codegen::visit(ObjectLiteral *ast)
+bool Codegen::visit(ObjectPattern *ast)
{
if (hasError)
return false;
+ QVector<QPair<Reference, ObjectPropertyValue>> computedProperties;
QMap<QString, ObjectPropertyValue> valueMap;
RegisterScope scope(this);
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString name = it->assignment->name->asString();
- if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
- Reference value = expression(nv->value);
- if (hasError)
- return false;
-
- ObjectPropertyValue &v = valueMap[name];
- if (v.hasGetter() || v.hasSetter() || (_context->isStrict && v.rvalue.isValid())) {
- throwSyntaxError(nv->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
-
- v.rvalue = value.storeOnStack();
- } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
- const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : nullptr);
- ObjectPropertyValue &v = valueMap[name];
- if (v.rvalue.isValid() ||
- (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) ||
- (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) {
- throwSyntaxError(gs->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
- if (gs->type == PropertyGetterSetter::Getter)
- v.getter = function;
- else
- v.setter = function;
- } else {
- Q_UNREACHABLE();
- }
- }
+ QStringList members;
- QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter;
- bool needSparseArray = false; // set to true if any array index is bigger than 16
-
- for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end();
- it != eit; ++it) {
- QString name = it.key();
- uint keyAsIndex = QV4::String::toArrayIndex(name);
- if (keyAsIndex != std::numeric_limits<uint>::max()) {
- it->keyAsIndex = keyAsIndex;
- if (keyAsIndex > 16)
- needSparseArray = true;
- if (it->hasSetter() || it->hasGetter())
- arrayKeyWithGetterSetter.append(name);
- else
- arrayKeyWithValue.append(name);
- } else {
- nonArrayKey.append(name);
- }
- }
-
- int args = -1;
- auto push = [this, &args](const Reference &arg) {
+ int argc = 0;
+ int args = 0;
+ auto push = [this, &args, &argc](const Reference &arg) {
int temp = bytecodeGenerator->newRegister();
- if (args == -1)
+ if (argc == 0)
args = temp;
(void) arg.storeOnStack(temp);
+ ++argc;
};
- QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members;
+ PatternPropertyList *it = ast->properties;
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ if (cname || p->type == PatternProperty::Getter || p->type == PatternProperty::Setter)
+ break;
+ QString name = p->name->asString();
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX)
+ break;
+ if (members.contains(name))
+ break;
+ members.append(name);
- Reference acc = Reference::fromAccumulator(this);
- // generate the key/value pairs
- for (const QString &key : qAsConst(nonArrayKey)) {
- const ObjectPropertyValue &prop = valueMap[key];
-
- if (prop.hasGetter() || prop.hasSetter()) {
- Q_ASSERT(!prop.rvalue.isValid());
- loadClosure(prop.getter);
- push(acc);
- loadClosure(prop.setter);
- push(acc);
- members.append({ key, true });
- } else {
- Q_ASSERT(prop.rvalue.isValid());
- push(prop.rvalue);
- members.append({ key, false });
+ {
+ RegisterScope innerScope(this);
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
}
- }
-
- // generate array entries with values
- for (const QString &key : qAsConst(arrayKeyWithValue)) {
- const ObjectPropertyValue &prop = valueMap[key];
- Q_ASSERT(!prop.hasGetter() && !prop.hasSetter());
- push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
- push(prop.rvalue);
- }
-
- // generate array entries with both a value and a setter
- for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) {
- const ObjectPropertyValue &prop = valueMap[key];
- Q_ASSERT(!prop.rvalue.isValid());
- push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
- loadClosure(prop.getter);
- push(acc);
- loadClosure(prop.setter);
- push(acc);
+ push(Reference::fromAccumulator(this));
}
int classId = jsUnitGenerator->registerJSClass(members);
- uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size();
- arrayGetterSetterCountAndFlags |= needSparseArray << 30;
-
- if (args == -1)
- args = 0;
+ // handle complex property setters
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ ObjectLiteralArgument argType = ObjectLiteralArgument::Value;
+ if (p->type == PatternProperty::Getter)
+ argType = ObjectLiteralArgument::Getter;
+ else if (p->type == PatternProperty::Setter)
+ argType = ObjectLiteralArgument::Setter;
+
+ Reference::fromConst(this, Encode(int(argType))).loadInAccumulator();
+ push(Reference::fromAccumulator(this));
+
+ if (cname) {
+ RegisterScope innerScope(this);
+ Reference name = expression(cname->expression);
+ if (hasError)
+ return false;
+ name.loadInAccumulator();
+ } else {
+ QString name = p->name->asString();
+#if 0
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX) {
+ Reference::fromConst(this, Encode(arrayIndex)).loadInAccumulator();
+ } else
+#endif
+ {
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(name);
+ bytecodeGenerator->addInstruction(instr);
+ }
+ }
+ push(Reference::fromAccumulator(this));
+ {
+ RegisterScope innerScope(this);
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
+ }
+ push(Reference::fromAccumulator(this));
+ }
Instruction::DefineObjectLiteral call;
call.internalClassId = classId;
- call.arrayValueCount = arrayKeyWithValue.size();
- call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags;
+ call.argc = argc;
call.args = Moth::StackSlot::createRegister(args);
bytecodeGenerator->addInstruction(call);
-
- _expr.setResult(Reference::fromAccumulator(this));
+ Reference result = Reference::fromAccumulator(this);
+ _expr.setResult(result);
return false;
}
@@ -1933,6 +2549,49 @@ bool Codegen::visit(StringLiteral *ast)
return false;
}
+bool Codegen::visit(TemplateLiteral *ast)
+{
+ if (hasError)
+ return false;
+
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(ast->value.toString());
+ bytecodeGenerator->addInstruction(instr);
+
+ if (ast->expression) {
+ RegisterScope scope(this);
+ int temp = bytecodeGenerator->newRegister();
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
+
+ Reference expr = expression(ast->expression);
+
+ if (ast->next) {
+ int temp2 = bytecodeGenerator->newRegister();
+ expr.storeOnStack(temp2);
+ visit(ast->next);
+
+ Instruction::Add instr;
+ instr.lhs = temp2;
+ bytecodeGenerator->addInstruction(instr);
+ } else {
+ expr.loadInAccumulator();
+ }
+
+ Instruction::Add instr;
+ instr.lhs = temp;
+ bytecodeGenerator->addInstruction(instr);
+ }
+
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+
+ _expr.setResult(r);
+ return false;
+
+}
+
bool Codegen::visit(ThisExpression *)
{
if (hasError)
@@ -2023,13 +2682,40 @@ bool Codegen::visit(FunctionDeclaration * ast)
RegisterScope scope(this);
- if (_context->compilationMode == QmlBinding)
- Reference::fromName(this, ast->name.toString()).loadInAccumulator();
+ if (_functionContext->contextType == ContextType::Binding)
+ referenceForName(ast->name.toString(), true).loadInAccumulator();
_expr.accept(nx);
return false;
}
-static bool endsWithReturn(Node *node)
+bool Codegen::visit(YieldExpression *ast)
+{
+ if (ast->isYieldStar) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield* is not currently supported"));
+ return false;
+ }
+ if (inFormalParameterList) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield is not allowed inside parameter lists"));
+ return false;
+ }
+
+
+ Reference result = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
+ if (hasError)
+ return false;
+ result.loadInAccumulator();
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
+ Instruction::Resume resume;
+ BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
+ Reference acc = Reference::fromAccumulator(this);
+ emitReturn(acc);
+ jump.link();
+ _expr.setResult(acc);
+ return false;
+}
+
+static bool endsWithReturn(Module *module, Node *node)
{
if (!node)
return false;
@@ -2038,32 +2724,29 @@ static bool endsWithReturn(Node *node)
if (AST::cast<ThrowStatement *>(node))
return true;
if (Program *p = AST::cast<Program *>(node))
- return endsWithReturn(p->elements);
- if (SourceElements *se = AST::cast<SourceElements *>(node)) {
- while (se->next)
- se = se->next;
- return endsWithReturn(se->element);
- }
- if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node))
- return endsWithReturn(sse->statement);
+ return endsWithReturn(module, p->statements);
if (StatementList *sl = AST::cast<StatementList *>(node)) {
while (sl->next)
sl = sl->next;
- return endsWithReturn(sl->statement);
+ return endsWithReturn(module, sl->statement);
+ }
+ if (Block *b = AST::cast<Block *>(node)) {
+ Context *blockContext = module->contextMap.value(node);
+ if (blockContext->requiresExecutionContext)
+ // we need to emit a return statement here, because of the
+ // unwind handler
+ return false;
+ return endsWithReturn(module, b->statements);
}
- if (Block *b = AST::cast<Block *>(node))
- return endsWithReturn(b->statements);
if (IfStatement *is = AST::cast<IfStatement *>(node))
- return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko);
+ return is->ko && endsWithReturn(module, is->ok) && endsWithReturn(module, is->ko);
return false;
}
int Codegen::defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body)
+ AST::StatementList *body)
{
- Q_UNUSED(formals);
-
enterContext(ast);
if (_context->functionIndex >= 0)
@@ -2074,7 +2757,15 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
_module->functions.append(_context);
_context->functionIndex = _module->functions.count() - 1;
- _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function
+ Context *savedFunctionContext = _functionContext;
+ _functionContext = _context;
+ ControlFlow *savedControlFlow = controlFlow;
+ controlFlow = nullptr;
+
+ if (_context->contextType == ContextType::Global) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
+ }
if (_module->debugMode) // allow the debugger to see overwritten arguments
_context->argumentsCanEscape = true;
@@ -2084,132 +2775,104 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
// at all, because if the onSignal is a signal handler, the user is actually making it explicit
// that the binding is a function, so we should execute that. However, we don't know that during
// AOT compilation, so mark the surrounding function as only-returning-a-closure.
- _context->returnsClosure = cast<ExpressionStatement *>(ast) && cast<FunctionExpression *>(cast<ExpressionStatement *>(ast)->expression);
+ _context->returnsClosure = body && body->statement && cast<ExpressionStatement *>(body->statement) && cast<FunctionExpression *>(cast<ExpressionStatement *>(body->statement)->expression);
BytecodeGenerator bytecode(_context->line, _module->debugMode);
BytecodeGenerator *savedBytecodeGenerator;
savedBytecodeGenerator = bytecodeGenerator;
bytecodeGenerator = &bytecode;
bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ BytecodeGenerator::Label *savedReturnLabel = _returnLabel;
+ _returnLabel = nullptr;
+
+ bool savedFunctionEndsWithReturn = functionEndsWithReturn;
+ functionEndsWithReturn = endsWithReturn(_module, body);
// reserve the js stack frame (Context & js Function & accumulator)
bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
+ bool _inFormalParameterList = false;
+ qSwap(_inFormalParameterList, inFormalParameterList);
+
int returnAddress = -1;
- bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode || _context->compilationMode == GlobalCode);
+ bool _requiresReturnValue = _context->requiresImplicitReturnValue();
qSwap(requiresReturnValue, _requiresReturnValue);
- if (requiresReturnValue)
- returnAddress = bytecodeGenerator->newRegister();
- if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
- _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
- if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed)
- _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope);
-
- bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval;
- bool needsCallContext = false;
- const QLatin1String exprForOn("expression for on");
- if (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && (_context->compilationMode != EvalCode || _context->isStrict))
- needsCallContext = true;
- else if (_context->compilationMode == QmlBinding && name.length() > exprForOn.size() && name.startsWith(exprForOn) && name.at(exprForOn.size()).isUpper())
- // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not.
- needsCallContext = true;
- if (needsCallContext) {
- Instruction::CreateCallContext createContext;
- bytecodeGenerator->addInstruction(createContext);
- }
-
- // variables in global code are properties of the global context object, not locals as with other functions.
- if (_context->compilationMode == FunctionCode || _context->compilationMode == QmlBinding) {
- for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) {
- const QString &local = it.key();
- if (allVarsEscape)
- it->canEscape = true;
- if (it->canEscape) {
- it->index = _context->locals.size();
- _context->locals.append(local);
- if (it->type == Context::ThisFunctionName) {
- // move the name from the stack to the call context
- Instruction::LoadReg load;
- load.reg = CallData::Function;
- bytecodeGenerator->addInstruction(load);
- Instruction::StoreLocal store;
- store.index = it->index;
- bytecodeGenerator->addInstruction(store);
- }
- } else {
- if (it->type == Context::ThisFunctionName)
- it->index = CallData::Function;
- else
- it->index = bytecodeGenerator->newRegister();
- }
- }
- } else {
- for (Context::MemberMap::const_iterator it = _context->members.constBegin(), cend = _context->members.constEnd(); it != cend; ++it) {
- const QString &local = it.key();
+ returnAddress = bytecodeGenerator->newRegister();
+ qSwap(_returnAddress, returnAddress);
- Instruction::DeclareVar declareVar;
- declareVar.isDeletable = false;
- declareVar.varName = registerString(local);
- bytecodeGenerator->addInstruction(declareVar);
- }
+ // register the lexical scope for global code
+ if (!_context->parent && _context->requiresExecutionContext) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
}
- qSwap(_returnAddress, returnAddress);
- for (const Context::Member &member : qAsConst(_context->members)) {
- if (member.function) {
- const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
- member.function->body ? member.function->body->elements : nullptr);
- loadClosure(function);
- if (! _context->parent) {
- Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator();
- } else {
- Q_ASSERT(member.index >= 0);
- Reference local = member.canEscape ? Reference::fromScopedLocal(this, member.index, 0)
- : Reference::fromStackSlot(this, member.index, true);
- local.storeConsumeAccumulator();
- }
+ RegisterScope registerScope(this);
+ _context->emitBlockHeader(this);
+
+ inFormalParameterList = true;
+ int argc = 0;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (!e) {
+ if (!formals->next)
+ // trailing comma
+ break;
+ Q_UNREACHABLE();
}
- }
- if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) {
- if (_context->isStrict) {
- Instruction::CreateUnmappedArgumentsObject setup;
- bytecodeGenerator->addInstruction(setup);
+
+ Reference arg = referenceForName(e->bindingIdentifier.toString(), true);
+ if (e->type == PatternElement::RestElement) {
+ Q_ASSERT(!formals->next);
+ Instruction::CreateRestParameter rest;
+ rest.argIndex = argc;
+ bytecodeGenerator->addInstruction(rest);
+ arg.storeConsumeAccumulator();
} else {
- Instruction::CreateMappedArgumentsObject setup;
- bytecodeGenerator->addInstruction(setup);
+ if (e->bindingTarget || e->initializer) {
+ initializeAndDestructureBindingElement(e, arg);
+ if (hasError)
+ break;
+ }
}
- referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
+ formals = formals->next;
+ ++argc;
}
- if (_context->usesThis && !_context->isStrict) {
- // make sure we convert this to an object
- Instruction::ConvertThisToObject convert;
- bytecodeGenerator->addInstruction(convert);
+ inFormalParameterList = false;
+
+ if (_context->isGenerator) {
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
}
beginFunctionBodyHook();
- sourceElements(body);
+ statementList(body);
- if (hasError || !endsWithReturn(body)) {
- bytecodeGenerator->setLocation(ast->lastSourceLocation());
- if (requiresReturnValue) {
- if (_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(_returnAddress);
- bytecodeGenerator->addInstruction(load);
- }
+ bytecodeGenerator->setLocation(ast->lastSourceLocation());
+ _context->emitBlockFooter(this);
+
+ if (_returnLabel || hasError || !functionEndsWithReturn) {
+ if (_returnLabel)
+ _returnLabel->link();
+
+ if (_returnLabel || requiresReturnValue) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(_returnAddress);
+ bytecodeGenerator->addInstruction(load);
} else {
Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
}
+
bytecodeGenerator->addInstruction(Instruction::Ret());
}
+ Q_ASSERT(_context == _functionContext);
bytecodeGenerator->finalize(_context);
- _context->registerCount = bytecodeGenerator->registerCount();
+ _context->registerCountInFunction = bytecodeGenerator->registerCount();
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
if (showCode) {
qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
- << "register count" << _context->registerCount;
+ << "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue;
QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(),
_context->line, _context->lineNumberMapping);
qDebug();
@@ -2217,29 +2880,17 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
qSwap(_returnAddress, returnAddress);
qSwap(requiresReturnValue, _requiresReturnValue);
+ qSwap(_inFormalParameterList, inFormalParameterList);
bytecodeGenerator = savedBytecodeGenerator;
+ delete _returnLabel;
+ _returnLabel = savedReturnLabel;
+ controlFlow = savedControlFlow;
+ functionEndsWithReturn = savedFunctionEndsWithReturn;
+ _functionContext = savedFunctionContext;
return leaveContext();
}
-bool Codegen::visit(FunctionSourceElement *ast)
-{
- if (hasError)
- return false;
-
- statement(ast->declaration);
- return false;
-}
-
-bool Codegen::visit(StatementSourceElement *ast)
-{
- if (hasError)
- return false;
-
- statement(ast->statement);
- return false;
-}
-
bool Codegen::visit(Block *ast)
{
if (hasError)
@@ -2247,6 +2898,7 @@ bool Codegen::visit(Block *ast)
RegisterScope scope(this);
+ ControlFlowBlock controlFlow(this, ast);
statementList(ast->statements);
return false;
}
@@ -2256,13 +2908,13 @@ bool Codegen::visit(BreakStatement *ast)
if (hasError)
return false;
- if (!_context->controlFlow) {
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
else
@@ -2270,7 +2922,7 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2282,13 +2934,13 @@ bool Codegen::visit(ContinueStatement *ast)
RegisterScope scope(this);
- if (!_context->controlFlow) {
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
else
@@ -2296,7 +2948,7 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2370,45 +3022,86 @@ bool Codegen::visit(ForEachStatement *ast)
RegisterScope scope(this);
- Reference nextIterObj = Reference::fromStackSlot(this);
- Reference iterObj = Reference::fromStackSlot(this);
- Reference expr = expression(ast->expression);
- if (hasError)
- return true;
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference lhsValue = Reference::fromStackSlot(this);
- expr.loadInAccumulator();
- Instruction::ForeachIteratorObject iteratorObjInstr;
- bytecodeGenerator->addInstruction(iteratorObjInstr);
- iterObj.storeConsumeAccumulator();
+ // There should be a temporal block, so that variables declared in lhs shadow outside vars.
+ // This block should define a temporal dead zone for those variables, which is not yet implemented.
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+ Reference expr = expression(ast->expression);
+ if (hasError)
+ return true;
- Reference lhs = expression(ast->initialiser).asLValue();
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = (ast->type == ForEachType::Of) ? 1 : 0;
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
- bytecodeGenerator->jump().link(in);
-
- ControlFlowLoop flow(this, &end, &in);
+ {
+ ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true);
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ // each iteration gets it's own context, as per spec
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ExpressionNode *e = ast->lhs->expressionCast()) {
+ if (AST::Pattern *p = e->patternCast()) {
+ RegisterScope scope(this);
+ destructurePattern(p, lhsValue);
+ } else {
+ Reference lhs = expression(e);
+ if (hasError)
+ goto error;
+ lhs = lhs.asLValue();
+ lhsValue.loadInAccumulator();
+ lhs.storeConsumeAccumulator();
+ }
+ } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
+ initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true);
+ if (hasError)
+ goto error;
+ } else {
+ Q_UNREACHABLE();
+ }
- BytecodeGenerator::Label body = bytecodeGenerator->label();
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- nextIterObj.loadInAccumulator();
- lhs.storeConsumeAccumulator();
+ }
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+ error:
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+ }
- in.link();
+ end.link();
- iterObj.loadInAccumulator();
- Instruction::ForeachNextPropertyName nextPropInstr;
- bytecodeGenerator->addInstruction(nextPropInstr);
- nextIterObj.storeConsumeAccumulator();
+ if (ast->type == ForEachType::Of) {
+ Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ }
- Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
- bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body);
-
- end.link();
+ done.link();
return false;
}
@@ -2420,7 +3113,12 @@ bool Codegen::visit(ForStatement *ast)
RegisterScope scope(this);
- statement(ast->initialiser);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ast->initialiser)
+ statement(ast->initialiser);
+ else if (ast->declarations)
+ variableDeclarationList(ast->declarations);
BytecodeGenerator::Label cond = bytecodeGenerator->label();
BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
@@ -2436,6 +3134,10 @@ bool Codegen::visit(ForStatement *ast)
setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
step.link();
+ if (_context->requiresExecutionContext) {
+ Instruction::CloneBlockContext clone;
+ bytecodeGenerator->addInstruction(clone);
+ }
statement(ast->expression);
bytecodeGenerator->jump().link(cond);
@@ -2458,7 +3160,7 @@ bool Codegen::visit(IfStatement *ast)
trueLabel.link();
statement(ast->ok);
if (ast->ko) {
- if (endsWithReturn(ast)) {
+ if (endsWithReturn(_module, ast)) {
falseLabel.link();
statement(ast->ko);
} else {
@@ -2482,7 +3184,7 @@ bool Codegen::visit(LabelledStatement *ast)
RegisterScope scope(this);
// check that no outer loop contains the label
- ControlFlow *l = _context->controlFlow;
+ ControlFlow *l = controlFlow;
while (l) {
if (l->label() == ast->label) {
QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
@@ -2497,9 +3199,7 @@ bool Codegen::visit(LabelledStatement *ast)
AST::cast<AST::WhileStatement *>(ast->statement) ||
AST::cast<AST::DoWhileStatement *>(ast->statement) ||
AST::cast<AST::ForStatement *>(ast->statement) ||
- AST::cast<AST::ForEachStatement *>(ast->statement) ||
- AST::cast<AST::LocalForStatement *>(ast->statement) ||
- AST::cast<AST::LocalForEachStatement *>(ast->statement)) {
+ AST::cast<AST::ForEachStatement *>(ast->statement)) {
statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop.
} else {
BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel();
@@ -2511,85 +3211,17 @@ bool Codegen::visit(LabelledStatement *ast)
return false;
}
-bool Codegen::visit(LocalForEachStatement *ast)
+void Codegen::emitReturn(const Reference &expr)
{
- if (hasError)
- return true;
-
- RegisterScope scope(this);
-
- Reference nextIterObj = Reference::fromStackSlot(this);
- Reference iterObj = Reference::fromStackSlot(this);
- Reference expr = expression(ast->expression);
- if (hasError)
- return true;
-
- variableDeclaration(ast->declaration);
-
- expr.loadInAccumulator();
- Instruction::ForeachIteratorObject iteratorObjInstr;
- bytecodeGenerator->addInstruction(iteratorObjInstr);
- iterObj.storeConsumeAccumulator();
-
- BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
-
- bytecodeGenerator->jump().link(in);
- ControlFlowLoop flow(this, &end, &in);
-
- BytecodeGenerator::Label body = bytecodeGenerator->label();
-
- Reference it = referenceForName(ast->declaration->name.toString(), true).asLValue();
-
- nextIterObj.loadInAccumulator();
- it.storeConsumeAccumulator();
-
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
-
- in.link();
-
- iterObj.loadInAccumulator();
- Instruction::ForeachNextPropertyName nextPropInstr;
- bytecodeGenerator->addInstruction(nextPropInstr);
- nextIterObj.storeConsumeAccumulator();
-
- Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
- bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body);
-
- end.link();
-
- return false;
-}
-
-bool Codegen::visit(LocalForStatement *ast)
-{
- if (hasError)
- return true;
-
- RegisterScope scope(this);
-
- variableDeclarationList(ast->declarations);
-
- BytecodeGenerator::Label cond = bytecodeGenerator->label();
- BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label step = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
-
- ControlFlowLoop flow(this, &end, &step);
-
- condition(ast->condition, &body, &end, true);
-
- body.link();
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
-
- step.link();
- statement(ast->expression);
- bytecodeGenerator->jump().link(cond);
- end.link();
-
- return false;
+ ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget();
+ if (target.linkLabel.isValid() && target.unwindLevel) {
+ Q_ASSERT(_returnAddress >= 0);
+ (void) expr.storeOnStack(_returnAddress);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
+ } else {
+ expr.loadInAccumulator();
+ bytecodeGenerator->addInstruction(Instruction::Ret());
+ }
}
bool Codegen::visit(ReturnStatement *ast)
@@ -2597,7 +3229,7 @@ bool Codegen::visit(ReturnStatement *ast)
if (hasError)
return true;
- if (_context->compilationMode != FunctionCode && _context->compilationMode != QmlBinding) {
+ if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) {
throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function"));
return false;
}
@@ -2610,17 +3242,8 @@ bool Codegen::visit(ReturnStatement *ast)
expr = Reference::fromConst(this, Encode::undefined());
}
- if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) {
- if (_returnAddress >= 0)
- (void) expr.storeOnStack(_returnAddress);
- else
- expr.loadInAccumulator();
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return);
- _context->controlFlow->jumpToHandler(h);
- } else {
- expr.loadInAccumulator();
- bytecodeGenerator->addInstruction(Instruction::Ret());
- }
+ emitReturn(expr);
+
return false;
}
@@ -2629,9 +3252,14 @@ bool Codegen::visit(SwitchStatement *ast)
if (hasError)
return true;
+ if (requiresReturnValue)
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+
RegisterScope scope(this);
if (ast->block) {
+ ControlFlowBlock controlFlow(this, ast->block);
+
BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel();
Reference lhs = expression(ast->expression);
@@ -2674,6 +3302,7 @@ bool Codegen::visit(SwitchStatement *ast)
ControlFlowLoop flow(this, &switchEnd);
+ insideSwitch = true;
for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
CaseClause *clause = it->clause;
blockMap[clause].link();
@@ -2694,6 +3323,7 @@ bool Codegen::visit(SwitchStatement *ast)
statementList(clause->statements);
}
+ insideSwitch = false;
switchEnd.link();
@@ -2713,25 +3343,15 @@ bool Codegen::visit(ThrowStatement *ast)
if (hasError)
return false;
- if (_context->controlFlow) {
- _context->controlFlow->handleThrow(expr);
- } else {
- expr.loadInAccumulator();
- Instruction::ThrowException instr;
- bytecodeGenerator->addInstruction(instr);
- }
+ expr.loadInAccumulator();
+ Instruction::ThrowException instr;
+ bytecodeGenerator->addInstruction(instr);
return false;
}
void Codegen::handleTryCatch(TryStatement *ast)
{
Q_ASSERT(ast);
- if (_context->isStrict &&
- (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) {
- throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
- return;
- }
-
RegisterScope scope(this);
BytecodeGenerator::Label noException = bytecodeGenerator->newLabel();
{
@@ -2761,8 +3381,6 @@ bool Codegen::visit(TryStatement *ast)
if (hasError)
return true;
- Q_ASSERT(_context->hasTry);
-
RegisterScope scope(this);
if (ast->finallyExpression && ast->finallyExpression->statement) {
@@ -2817,17 +3435,18 @@ bool Codegen::visit(WithStatement *ast)
RegisterScope scope(this);
- _context->hasWith = true;
-
Reference src = expression(ast->expression);
if (hasError)
return false;
src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place
src.loadInAccumulator();
- ControlFlowWith flow(this);
-
- statement(ast->statement);
+ enterContext(ast);
+ {
+ ControlFlowWith flow(this);
+ statement(ast->statement);
+ }
+ leaveContext();
return false;
}
@@ -3080,6 +3699,22 @@ Codegen::RValue Codegen::RValue::storeOnStack() const
}
}
+void Codegen::RValue::loadInAccumulator() const
+{
+ switch (type) {
+ case Accumulator:
+ // nothing to do
+ return;
+ case StackSlot:
+ return Reference::fromStackSlot(codegen, theStackSlot).loadInAccumulator();
+ case Const:
+ return Reference::fromConst(codegen, constant).loadInAccumulator();
+ default:
+ Q_UNREACHABLE();
+ }
+
+}
+
Codegen::Reference::Reference(const Codegen::Reference &other)
{
*this = other;
@@ -3093,6 +3728,11 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
case Invalid:
case Accumulator:
break;
+ case Super:
+ break;
+ case SuperProperty:
+ property = other.property;
+ break;
case StackSlot:
theStackSlot = other.theStackSlot;
break;
@@ -3127,6 +3767,7 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
isArgOrEval = other.isArgOrEval;
codegen = other.codegen;
isReadonly = other.isReadonly;
+ isReferenceToConst = other.isReferenceToConst;
stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument;
isVolatile = other.isVolatile;
global = other.global;
@@ -3141,6 +3782,10 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const
case Invalid:
case Accumulator:
break;
+ case Super:
+ return true;
+ case SuperProperty:
+ return property == other.property;
case StackSlot:
return theStackSlot == other.theStackSlot;
case ScopedLocal:
@@ -3184,6 +3829,9 @@ Codegen::Reference Codegen::Reference::asLValue() const
case Invalid:
case Accumulator:
Q_UNREACHABLE();
+ case Super:
+ codegen->throwSyntaxError(AST::SourceLocation(), QStringLiteral("Super lvalues not implemented."));
+ return *this;
case Member:
if (!propertyBase.isStackSlot()) {
Reference r = *this;
@@ -3209,6 +3857,28 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
return Reference();
}
+Codegen::Reference Codegen::Reference::baseObject() const
+{
+ if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) {
+ return Reference::fromStackSlot(codegen, qmlBase.stackSlot());
+ } else if (type == Reference::Member) {
+ RValue rval = propertyBase;
+ if (!rval.isValid())
+ return Reference::fromConst(codegen, Encode::undefined());
+ if (rval.isAccumulator())
+ return Reference::fromAccumulator(codegen);
+ if (rval.isStackSlot())
+ Reference::fromStackSlot(codegen, rval.stackSlot());
+ if (rval.isConst())
+ return Reference::fromConst(codegen, rval.constantValue());
+ Q_UNREACHABLE();
+ } else if (type == Reference::Subscript) {
+ return Reference::fromStackSlot(codegen, elementBase.stackSlot());
+ } else {
+ return Reference::fromConst(codegen, Encode::undefined());
+ }
+}
+
Codegen::Reference Codegen::Reference::storeOnStack() const
{ return doStoreOnStack(-1); }
@@ -3230,7 +3900,7 @@ Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
}
Reference slot = Reference::fromStackSlot(codegen, slotIndex);
- if (isConst()) {
+ if (isConstant()) {
Instruction::MoveConst move;
move.constIndex = codegen->registerConstant(constant);
move.destTemp = slot.stackSlot();
@@ -3280,7 +3950,29 @@ bool Codegen::Reference::storeWipesAccumulator() const
void Codegen::Reference::storeAccumulator() const
{
+ if (isReferenceToConst) {
+ // throw a type error
+ RegisterScope scope(codegen);
+ Reference r = codegen->referenceForName(QStringLiteral("TypeError"), false);
+ r = r.storeOnStack();
+ Instruction::Construct construct;
+ construct.func = r.stackSlot();
+ construct.argc = 0;
+ construct.argv = 0;
+ codegen->bytecodeGenerator->addInstruction(construct);
+ Instruction::ThrowException throwException;
+ codegen->bytecodeGenerator->addInstruction(throwException);
+ return;
+ }
switch (type) {
+ case Super:
+ Q_UNREACHABLE();
+ return;
+ case SuperProperty:
+ Instruction::StoreSuperProperty store;
+ store.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(store);
+ return;
case StackSlot: {
Instruction::StoreReg store;
store.reg = theStackSlot;
@@ -3313,7 +4005,7 @@ void Codegen::Reference::storeAccumulator() const
}
} return;
case Member:
- if (codegen->useFastLookups) {
+ if (!disable_lookups && codegen->useFastLookups) {
Instruction::SetLookup store;
store.base = propertyBase.stackSlot();
store.index = codegen->registerSetterLookup(propertyNameIndex);
@@ -3358,6 +4050,14 @@ void Codegen::Reference::loadInAccumulator() const
switch (type) {
case Accumulator:
return;
+ case Super:
+ Q_UNREACHABLE();
+ return;
+ case SuperProperty:
+ Instruction::LoadSuperProperty load;
+ load.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
case Const: {
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
@@ -3429,7 +4129,7 @@ QT_WARNING_POP
return;
}
}
- if (codegen->useFastLookups && global) {
+ if (!disable_lookups && codegen->useFastLookups && global) {
Instruction::LoadGlobalLookup load;
load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
codegen->bytecodeGenerator->addInstruction(load);
@@ -3440,46 +4140,23 @@ QT_WARNING_POP
}
return;
case Member:
- if (codegen->useFastLookups) {
- if (propertyBase.isAccumulator()) {
- Instruction::GetLookupA load;
- load.index = codegen->registerGetterLookup(propertyNameIndex);
- codegen->bytecodeGenerator->addInstruction(load);
- } else {
- Instruction::GetLookup load;
- load.base = propertyBase.storeOnStack().stackSlot();
- load.index = codegen->registerGetterLookup(propertyNameIndex);
- codegen->bytecodeGenerator->addInstruction(load);
- }
- } else {
- if (propertyBase.isAccumulator()) {
- Instruction::LoadPropertyA load;
- load.name = propertyNameIndex;
- codegen->bytecodeGenerator->addInstruction(load);
- } else {
- Instruction::LoadProperty load;
- load.base = propertyBase.storeOnStack().stackSlot();
- load.name = propertyNameIndex;
- codegen->bytecodeGenerator->addInstruction(load);
- }
- }
- return;
- case Subscript: {
- if (elementSubscript.isAccumulator()) {
- Instruction::LoadElementA load;
- load.base = elementBase;
- codegen->bytecodeGenerator->addInstruction(load);
- } else if (elementSubscript.isConst()) {
- Reference::fromConst(codegen, elementSubscript.constantValue()).loadInAccumulator();
- Instruction::LoadElementA load;
- load.base = elementBase;
+ if (!disable_lookups && codegen->useFastLookups) {
+ propertyBase.loadInAccumulator();
+ Instruction::GetLookup load;
+ load.index = codegen->registerGetterLookup(propertyNameIndex);
codegen->bytecodeGenerator->addInstruction(load);
} else {
- Instruction::LoadElement load;
- load.base = elementBase;
- load.index = elementSubscript.storeOnStack().stackSlot();
+ propertyBase.loadInAccumulator();
+ Instruction::LoadProperty load;
+ load.name = propertyNameIndex;
codegen->bytecodeGenerator->addInstruction(load);
}
+ return;
+ case Subscript: {
+ elementSubscript.loadInAccumulator();
+ Instruction::LoadElement load;
+ load.base = elementBase;
+ codegen->bytecodeGenerator->addInstruction(load);
} return;
case QmlScopeObject: {
Instruction::LoadScopeObjectProperty load;
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index d51dc29517..337c4dbfe3 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -66,6 +66,7 @@
#endif
#include <private/qv4util_p.h>
#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4stackframe_p.h>
QT_BEGIN_NAMESPACE
@@ -101,7 +102,7 @@ public:
const QString &sourceCode,
AST::Program *ast,
Module *module,
- CompilationMode mode = GlobalCode);
+ ContextType contextType = ContextType::Global);
public:
class VolatileMemoryLocationScanner;
@@ -172,11 +173,14 @@ public:
}
Q_REQUIRED_RESULT RValue storeOnStack() const;
+ void loadInAccumulator() const;
};
struct Reference {
enum Type {
Invalid,
Accumulator,
+ Super,
+ SuperProperty,
StackSlot,
ScopedLocal,
Name,
@@ -197,6 +201,8 @@ public:
Reference &operator =(const Reference &other);
bool operator==(const Reference &other) const;
+ bool operator!=(const Reference &other) const
+ { return !(*this == other); }
bool isValid() const { return type != Invalid; }
bool loadTriggersSideEffect() const {
@@ -209,8 +215,10 @@ public:
return false;
}
}
- bool isConst() const { return type == Const; }
+ bool isConstant() const { return type == Const; }
bool isAccumulator() const { return type == Accumulator; }
+ bool isSuper() const { return type == Super; }
+ bool isSuperProperty() const { return type == SuperProperty; }
bool isStackSlot() const { return type == StackSlot; }
bool isRegister() const {
return isStackSlot();
@@ -241,6 +249,9 @@ public:
static Reference fromAccumulator(Codegen *cg) {
return Reference(cg, Accumulator);
}
+ static Reference fromSuper(Codegen *cg) {
+ return Reference(cg, Super);
+ }
static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) {
Reference r(cg, StackSlot);
if (tempIndex == -1)
@@ -273,6 +284,12 @@ public:
r.propertyNameIndex = r.codegen->registerString(name);
return r;
}
+ static Reference fromSuperProperty(const Reference &property) {
+ Q_ASSERT(property.isStackSlot());
+ Reference r(property.codegen, SuperProperty);
+ r.property = property.stackSlot();
+ return r;
+ }
static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) {
Q_ASSERT(baseRef.isStackSlot());
Reference r(baseRef.codegen, Subscript);
@@ -322,6 +339,8 @@ public:
Q_REQUIRED_RESULT Reference storeRetainAccumulator() const;
Reference storeConsumeAccumulator() const;
+ Q_REQUIRED_RESULT Reference baseObject() const;
+
bool storeWipesAccumulator() const;
void loadInAccumulator() const;
@@ -357,10 +376,12 @@ public:
qint16 qmlNotifyIndex;
PropertyCapturePolicy capturePolicy;
};
+ Moth::StackSlot property; // super property
};
QString name;
mutable bool isArgOrEval = false;
bool isReadonly = false;
+ bool isReferenceToConst = false;
bool stackSlotIsLocalOrArgument = false;
bool isVolatile = false;
bool global = false;
@@ -464,7 +485,10 @@ protected:
void enterContext(AST::Node *node);
int leaveContext();
-
+public:
+ Context *enterBlock(AST::Node *node);
+ int leaveBlock() { return leaveContext(); }
+protected:
void leaveLoop();
enum UnaryOperation {
@@ -482,6 +506,7 @@ protected:
void addCJump();
+public:
int registerString(const QString &name) {
return jsUnitGenerator->registerString(name);
}
@@ -493,33 +518,37 @@ protected:
// Returns index in _module->functions
virtual int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body);
+ AST::StatementList *body);
+protected:
void statement(AST::Statement *ast);
void statement(AST::ExpressionNode *ast);
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
Reference expression(AST::ExpressionNode *ast);
- Result sourceElement(AST::SourceElement *ast);
void accept(AST::Node *node);
- void functionBody(AST::FunctionBody *ast);
void program(AST::Program *ast);
- void sourceElements(AST::SourceElements *ast);
void statementList(AST::StatementList *ast);
- void variableDeclaration(AST::VariableDeclaration *ast);
+ void variableDeclaration(AST::PatternElement *ast);
void variableDeclarationList(AST::VariableDeclarationList *ast);
- Reference referenceForName(const QString &name, bool lhs);
+ Reference targetForPatternElement(AST::PatternElement *p);
+ void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false);
+ void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false);
+ void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false);
+ void destructurePattern(AST::Pattern *p, const Reference &rhs);
- void loadClosure(int index);
+ Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
// Hook provided to implement QML lookup semantics
virtual Reference fallbackNameLookup(const QString &name);
virtual void beginFunctionBodyHook() {}
+ void emitReturn(const Reference &expr);
+
// nodes
bool visit(AST::ArgumentList *ast) override;
bool visit(AST::CaseBlock *ast) override;
@@ -527,16 +556,10 @@ protected:
bool visit(AST::CaseClauses *ast) override;
bool visit(AST::Catch *ast) override;
bool visit(AST::DefaultClause *ast) override;
- bool visit(AST::ElementList *ast) override;
bool visit(AST::Elision *ast) override;
bool visit(AST::Finally *ast) override;
bool visit(AST::FormalParameterList *ast) override;
- bool visit(AST::FunctionBody *ast) override;
bool visit(AST::Program *ast) override;
- bool visit(AST::PropertyNameAndValue *ast) override;
- bool visit(AST::PropertyAssignmentList *ast) override;
- bool visit(AST::PropertyGetterSetter *ast) override;
- bool visit(AST::SourceElements *ast) override;
bool visit(AST::StatementList *ast) override;
bool visit(AST::UiArrayMemberList *ast) override;
bool visit(AST::UiImport *ast) override;
@@ -548,19 +571,25 @@ protected:
bool visit(AST::UiProgram *ast) override;
bool visit(AST::UiQualifiedId *ast) override;
bool visit(AST::UiQualifiedPragmaId *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
bool visit(AST::VariableDeclarationList *ast) override;
+ bool visit(AST::PatternElement *ast) override;
+ bool visit(AST::PatternElementList *ast) override;
+ bool visit(AST::PatternProperty *ast) override;
+ bool visit(AST::PatternPropertyList *ast) override;
+
// expressions
bool visit(AST::Expression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
+ bool visit(AST::ArrayPattern *ast) override;
bool visit(AST::ArrayMemberExpression *ast) override;
bool visit(AST::BinaryExpression *ast) override;
bool visit(AST::CallExpression *ast) override;
bool visit(AST::ConditionalExpression *ast) override;
bool visit(AST::DeleteExpression *ast) override;
bool visit(AST::FalseLiteral *ast) override;
+ bool visit(AST::SuperLiteral *ast) override;
bool visit(AST::FieldMemberExpression *ast) override;
+ bool visit(AST::TaggedTemplate *ast) override;
bool visit(AST::FunctionExpression *ast) override;
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::NestedExpression *ast) override;
@@ -569,13 +598,14 @@ protected:
bool visit(AST::NotExpression *ast) override;
bool visit(AST::NullExpression *ast) override;
bool visit(AST::NumericLiteral *ast) override;
- bool visit(AST::ObjectLiteral *ast) override;
+ bool visit(AST::ObjectPattern *ast) override;
bool visit(AST::PostDecrementExpression *ast) override;
bool visit(AST::PostIncrementExpression *ast) override;
bool visit(AST::PreDecrementExpression *ast) override;
bool visit(AST::PreIncrementExpression *ast) override;
bool visit(AST::RegExpLiteral *ast) override;
bool visit(AST::StringLiteral *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::TildeExpression *ast) override;
bool visit(AST::TrueLiteral *ast) override;
@@ -584,10 +614,9 @@ protected:
bool visit(AST::UnaryPlusExpression *ast) override;
bool visit(AST::VoidExpression *ast) override;
bool visit(AST::FunctionDeclaration *ast) override;
-
- // source elements
- bool visit(AST::FunctionSourceElement *ast) override;
- bool visit(AST::StatementSourceElement *ast) override;
+ bool visit(AST::YieldExpression *ast) override;
+ bool visit(AST::ClassExpression *ast) override;
+ bool visit(AST::ClassDeclaration *ast) override;
// statements
bool visit(AST::Block *ast) override;
@@ -601,8 +630,6 @@ protected:
bool visit(AST::ForStatement *ast) override;
bool visit(AST::IfStatement *ast) override;
bool visit(AST::LabelledStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
bool visit(AST::ReturnStatement *ast) override;
bool visit(AST::SwitchStatement *ast) override;
bool visit(AST::ThrowStatement *ast) override;
@@ -631,18 +658,36 @@ public:
Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right);
Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right);
- struct Arguments { int argc; int argv; };
+ struct Arguments { int argc; int argv; bool hasSpread; };
Arguments pushArgs(AST::ArgumentList *args);
+ void handleCall(Reference &base, Arguments calldata);
+
+ Arguments pushTemplateArgs(AST::TemplateLiteral *args);
+ int createTemplateArray(AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
void handleTryCatch(AST::TryStatement *ast);
void handleTryFinally(AST::TryStatement *ast);
+
+ Reference referenceForName(const QString &name, bool lhs);
+
QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true);
static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
Context *currentContext() const { return _context; }
+ BytecodeGenerator *generator() const { return bytecodeGenerator; }
+
+ void loadClosure(int index);
+
+ Module *module() const { return _module; }
+
+ BytecodeGenerator::Label returnLabel() {
+ if (!_returnLabel)
+ _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel());
+ return *_returnLabel;
+ }
protected:
friend class ScanFunctions;
@@ -650,16 +695,22 @@ protected:
friend struct ControlFlowCatch;
friend struct ControlFlowFinally;
Result _expr;
- VolatileMemoryLocations _volataleMemoryLocations;
+ VolatileMemoryLocations _volatileMemoryLocations;
Module *_module;
int _returnAddress;
Context *_context;
+ Context *_functionContext = nullptr;
AST::LabelledStatement *_labelledStatement;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
BytecodeGenerator *bytecodeGenerator = nullptr;
+ Moth::BytecodeGenerator::Label *_returnLabel = nullptr;
bool _strictMode;
bool useFastLookups = true;
bool requiresReturnValue = false;
+ bool insideSwitch = false;
+ bool inFormalParameterList = false;
+ bool functionEndsWithReturn = false;
+ ControlFlow *controlFlow = nullptr;
bool _fileNameIsUrl;
bool hasError;
@@ -667,6 +718,7 @@ protected:
private:
VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
+ void handleConstruct(const Reference &base, AST::ArgumentList *args);
};
}
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
index 8348613888..1fef4d38f4 100644
--- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
@@ -92,8 +92,16 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- munmap(dataPtr, length);
+ // Do not unmap the data here.
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ munmap(dataPtr, length);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp
index 8b000021f8..3e44d045fc 100644
--- a/src/qml/compiler/qv4compilationunitmapper_win.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp
@@ -90,14 +90,9 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
if (!header.verifyHeader(sourceTimeStamp, errorString))
return nullptr;
- const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? PAGE_EXECUTE_READ : PAGE_READONLY;
- const uint viewFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? (FILE_MAP_READ | FILE_MAP_EXECUTE) : FILE_MAP_READ;
-
// Data structure and qt version matched, so now we can access the rest of the file safely.
- HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0);
+ HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0);
if (!fileMappingHandle) {
*errorString = qt_error_string(GetLastError());
return nullptr;
@@ -107,7 +102,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
CloseHandle(fileMappingHandle);
});
- dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0);
+ dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
if (!dataPtr) {
*errorString = qt_error_string(GetLastError());
return nullptr;
@@ -118,8 +113,15 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- UnmapViewOfFile(dataPtr);
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ UnmapViewOfFile(dataPtr);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 8dcc068a06..02aee47eea 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -78,26 +78,10 @@ namespace CompiledData {
static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
-#if !defined(V4_BOOTSTRAP)
-static QString cacheFilePath(const QUrl &url)
+CompilationUnit::CompilationUnit(const Unit *unitData)
{
- const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
- const QString localCachePath = localSourcePath + QLatin1Char('c');
-#ifndef Q_OS_ANDROID
- if (QFile::exists(localCachePath) || QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable())
- return localCachePath;
-#endif
- QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
- fileNameHash.addData(localSourcePath.toUtf8());
- QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
- QDir::root().mkpath(directory);
- return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix();
+ data = unitData;
}
-#endif
-
-CompilationUnit::CompilationUnit(const Unit *unitData)
- : data(unitData)
-{}
#ifndef V4_BOOTSTRAP
CompilationUnit::~CompilationUnit()
@@ -108,6 +92,17 @@ CompilationUnit::~CompilationUnit()
data = nullptr;
}
+QString CompilationUnit::localCacheFilePath(const QUrl &url)
+{
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
+}
+
QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
{
this->engine = engine;
@@ -157,15 +152,15 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
}
if (data->jsClassTableSize) {
- runtimeClasses = (QV4::InternalClass**)malloc(data->jsClassTableSize * sizeof(QV4::InternalClass*));
+ runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ // memset the regexps to 0 in case a GC run happens while we're within the loop below
+ memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
- QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object];
+ runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
for (int j = 0; j < memberCount; ++j, ++member)
- klass = klass->addMember(engine->identifierTable->identifier(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
-
- runtimeClasses[i] = klass;
+ runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
}
}
@@ -215,8 +210,6 @@ void CompilationUnit::unlink()
propertyCaches.clear();
- for (int ii = 0; ii < dependentScripts.count(); ++ii)
- dependentScripts.at(ii)->release();
dependentScripts.clear();
typeNameCache = nullptr;
@@ -244,29 +237,43 @@ void CompilationUnit::unlink()
void CompilationUnit::markObjects(QV4::MarkStack *markStack)
{
- for (uint i = 0; i < data->stringTableSize; ++i)
- if (runtimeStrings[i])
- runtimeStrings[i]->mark(markStack);
+ if (runtimeStrings) {
+ for (uint i = 0; i < data->stringTableSize; ++i)
+ if (runtimeStrings[i])
+ runtimeStrings[i]->mark(markStack);
+ }
if (runtimeRegularExpressions) {
for (uint i = 0; i < data->regexpTableSize; ++i)
runtimeRegularExpressions[i].mark(markStack);
}
+ if (runtimeClasses) {
+ for (uint i = 0; i < data->jsClassTableSize; ++i)
+ if (runtimeClasses[i])
+ runtimeClasses[i]->mark(markStack);
+ }
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ if (f && f->internalClass)
+ f->internalClass->mark(markStack);
+ for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks))
+ if (c)
+ c->mark(markStack);
+
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i)
+ runtimeLookups[i].markObjects(markStack);
+ }
}
-IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
{
- auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
- if (it == namedObjectsPerComponentCache.end()) {
- IdentifierHash namedObjectCache(engine);
- const CompiledData::Object *component = data->objectAt(componentObjectIndex);
- const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
- for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
- const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr);
- namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
- }
- it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
- }
- return *it;
+ IdentifierHash namedObjectCache(engine);
+ const CompiledData::Object *component = data->objectAt(componentObjectIndex);
+ const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
+ for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
+ const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
+ }
+ return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
}
void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
@@ -344,7 +351,11 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS
const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
- CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourceTimeStamp, errorString);
+ QString cachePath = sourcePath + QLatin1Char('c');
+ if (!QFile::exists(cachePath))
+ cachePath = localCacheFilePath(url);
+
+ CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString);
if (!mappedUnit)
return false;
@@ -367,7 +378,22 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
runtimeFunctions.resize(data->functionTableSize);
for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
- runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec);
+ runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction);
+ }
+
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope);
+
+ runtimeBlocks.resize(data->blockTableSize);
+ for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
+ const QV4::CompiledData::Block *compiledBlock = data->blockAt(i);
+ ic = engine->internalClasses(EngineBase::Class_CallContext);
+
+ // first locals
+ const quint32_le *localsIndices = compiledBlock->localsTable();
+ for (quint32 i = 0; i < compiledBlock->nLocals; ++i)
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ runtimeBlocks[i] = ic->d();
}
}
@@ -391,7 +417,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
- const QString outputFileName = cacheFilePath(unitUrl);
+ const QString outputFileName = localCacheFilePath(unitUrl);
#endif
#if QT_CONFIG(temporaryfile)
@@ -476,8 +502,11 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals;
changedSignalParameters << parameters;
- for (; parameters; parameters = parameters->next)
- stringTable.registerString(parameters->name.toString());
+ if (parameters) {
+ const QStringList formals = parameters->formals();
+ for (const QString &arg : formals)
+ stringTable.registerString(arg);
+ }
}
}
@@ -500,14 +529,14 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex];
- for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i);
- parameters; parameters = parameters->next) {
- signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString()));
- function->nFormals = function->nFormals + 1;
- }
+ if (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i)) {
+ const QStringList formals = parameters->formals();
+ for (const QString &arg : formals)
+ signalParameterNameTable.append(stringTable.getStringId(arg));
- // Hack to ensure an activation is created.
- function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval;
+ function->nFormals = formals.size();
+ }
+ function->length = function->nFormals;
signalParameterNameTableOffset += function->nFormals * sizeof(quint32);
}
@@ -626,7 +655,7 @@ QString Binding::valueAsScriptString(const Unit *unit) const
/*!
Returns the property cache, if one alread exists. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const
{
if (type.isValid())
return typePropertyCache;
@@ -637,7 +666,7 @@ QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
/*!
Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
{
if (typePropertyCache) {
return typePropertyCache;
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 1df9d6794f..c1be00ea27 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -186,32 +186,72 @@ struct JSClass
};
static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+// This data structure is intended to be binary compatible with QStringData/QStaticStringData on
+// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped
+// from a file must be castable to a QStringData regardless of the pointer size. With the first
+// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a
+// ptrdiff_t and thus variable in size.
+// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while
+// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain
+// the same value.
struct String
{
+ qint32_le refcount; // -1
qint32_le size;
+ quint32_le allocAndCapacityReservedFlag; // 0
+ quint32_le offsetOn32Bit;
+ quint64_le offsetOn64Bit;
// uint16 strdata[]
static int calculateSize(const QString &str) {
- return (sizeof(String) + str.length() * sizeof(quint16) + 7) & ~0x7;
+ return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7;
}
};
-static_assert(sizeof(String) == 4, "String 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(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+// Ensure compatibility with QString
+static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location");
+static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location");
+static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location");
+static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location");
+#if QT_POINTER_SIZE == 8
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location");
+#else
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location");
+#endif
struct CodeOffsetToLine {
quint32_le codeOffset;
quint32_le line;
};
+struct Block
+{
+ quint32_le nLocals;
+ quint32_le localsOffset;
+
+ const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
+
+ static int calculateSize(int nLocals) {
+ int trailingData = nLocals*sizeof (quint32);
+ size_t size = align(align(sizeof(Block)) + size_t(trailingData));
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
struct Function
{
enum Flags : unsigned int {
IsStrict = 0x1,
- HasDirectEval = 0x2,
- UsesArgumentsObject = 0x4,
-// Unused = 0x8,
- HasCatchOrWith = 0x10
+ IsArrowFunction = 0x2,
+ IsGenerator = 0x4
};
// Absolute offset into file where the code for this function is located.
@@ -219,6 +259,7 @@ struct Function
quint32_le codeSize;
quint32_le nameIndex;
+ quint32_le length;
quint32_le nFormals;
quint32_le formalsOffset;
quint32_le nLocals;
@@ -238,16 +279,14 @@ struct Function
quint32_le dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
// Qml Extensions End
-// quint32 formalsIndex[nFormals]
-// quint32 localsIndex[nLocals]
-// quint32 offsetForInnerFunctions[nInnerFunctions]
-// Function[nInnerFunctions]
-
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
quint16_le padding2;
+ // quint32 formalsIndex[nFormals]
+ // quint32 localsIndex[nLocals]
+
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); }
@@ -260,7 +299,7 @@ struct Function
const quint32_le *formalsEnd() const { return formalsTable() + nFormals; }
// ---
- const uchar *code() const { return reinterpret_cast<const uchar *>(this) + codeOffset; }
+ const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
@@ -276,7 +315,44 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 76, "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) == 80, "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 {
+ Regular,
+ Getter,
+ Setter
+ };
+
+ quint32_le name;
+ quint32_le type;
+ quint32_le function;
+};
+
+struct Class
+{
+ quint32_le nameIndex;
+ quint32_le scopeIndex;
+ quint32_le constructorFunction;
+ quint32_le nStaticMethods;
+ quint32_le nMethods;
+ quint32_le methodTableOffset;
+
+ const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); }
+
+ static int calculateSize(int nStaticMethods, int nMethods) {
+ int trailingData = (nStaticMethods + nMethods) * sizeof(Method);
+ size_t size = align(sizeof(Class) + trailingData);
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+static_assert(sizeof(Class) == 24, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
// Qml data structures
@@ -385,8 +461,8 @@ struct Q_QML_PRIVATE_EXPORT Binding
static QString escapedString(const QString &string);
- bool containsTranslations() const { return type == Type_Translation || type == Type_TranslationById; }
- bool evaluatesToString() const { return type == Type_String || containsTranslations(); }
+ bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
+ bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
QString valueAsString(const Unit *unit) const;
QString valueAsScriptString(const Unit *unit) const;
@@ -701,14 +777,17 @@ struct Unit
StaticData = 0x4, // Unit data persistent in memory?
IsSingleton = 0x8,
IsSharedLibrary = 0x10, // .pragma shared?
- ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions
- PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation
+ PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation
};
quint32_le flags;
quint32_le stringTableSize;
quint32_le offsetToStringTable;
quint32_le functionTableSize;
quint32_le offsetToFunctionTable;
+ quint32_le classTableSize;
+ quint32_le offsetToClassTable;
+ quint32_le blockTableSize;
+ quint32_le offsetToBlockTable;
quint32_le lookupTableSize;
quint32_le offsetToLookupTable;
quint32_le regexpTableSize;
@@ -753,11 +832,11 @@ struct Unit
if (str->size == 0)
return QString();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (flags & StaticData) {
+ const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) };
+ return QString(holder);
+ }
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
- // Too risky to do this while we unmap disk backed compilation but keep pointers to string
- // data in the identifier tables.
- // if (flags & StaticData)
- // return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
#else
const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1);
@@ -770,6 +849,8 @@ struct Unit
}
const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); }
+ const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
const quint32_le *offsetTable = functionOffsetTable();
@@ -777,6 +858,18 @@ struct Unit
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
+ const Class *classAt(int idx) const {
+ const quint32_le *offsetTable = classOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
+ const Block *blockAt(int idx) const {
+ const quint32_le *offsetTable = blockOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); }
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
@@ -795,7 +888,7 @@ struct Unit
}
};
-static_assert(sizeof(Unit) == 192, "Unit 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(Unit) == 208, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TypeReference
{
@@ -832,9 +925,7 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
if (prop->type >= QV4::CompiledData::Property::Custom) {
- // ### FIXME: We could report the more accurate location here by using prop->location, but the old
- // compiler can't and the tests expect it to be the object location right now.
- TypeReference &r = this->add(prop->customTypeNameIndex, obj->location);
+ TypeReference &r = this->add(prop->customTypeNameIndex, prop->location);
r.errorWhenNotFound = true;
}
}
@@ -881,6 +972,8 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
QV4::Heap::String **runtimeStrings = nullptr; // Array
const Value* constants = nullptr;
QV4::Value *runtimeRegularExpressions = nullptr;
+ const Unit *data = nullptr;
+ QV4::Heap::InternalClass **runtimeClasses = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
@@ -915,8 +1008,6 @@ public:
return refCount.load();
}
- const Unit *data = nullptr;
-
// Called only when building QML, when we build the header for JS first and append QML data
QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
@@ -943,14 +1034,14 @@ public:
}
QV4::Lookup *runtimeLookups = nullptr;
- QV4::InternalClass **runtimeClasses = nullptr;
QVector<QV4::Function *> runtimeFunctions;
+ QVector<QV4::Heap::InternalClass *> runtimeBlocks;
mutable QQmlNullableValue<QUrl> m_url;
mutable QQmlNullableValue<QUrl> m_finalUrl;
// QML specific fields
QQmlPropertyCacheVector propertyCaches;
- QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
+ QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
@@ -962,7 +1053,7 @@ public:
// mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
// this is initialized on-demand by QQmlContextData
QHash<int, IdentifierHash> namedObjectsPerComponentCache;
- IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
+ inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
@@ -970,7 +1061,7 @@ public:
int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
int totalObjectCount = 0; // Number of objects explicitly instantiated
- QVector<QQmlScriptData *> dependentScripts;
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
ResolvedTypeReferenceMap resolvedTypes;
bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const;
@@ -1010,6 +1101,8 @@ public:
bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
+ static QString localCacheFilePath(const QUrl &url);
+
protected:
void linkBackendToEngine(QV4::ExecutionEngine *engine);
#endif // V4_BOOTSTRAP
@@ -1019,6 +1112,8 @@ private:
QAtomicInt refCount = 1;
+ Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
+
public:
#if defined(V4_BOOTSTRAP)
bool saveToDisk(const QString &outputFileName, QString *errorString);
@@ -1046,17 +1141,24 @@ struct ResolvedTypeReference
// therefore cannot have a property cache installed when instantiated.
bool isFullyDynamicType;
- QQmlPropertyCache *propertyCache() const;
- QQmlPropertyCache *createPropertyCache(QQmlEngine *);
+ QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
+ QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *);
bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
void doDynamicTypeCheck();
};
-#endif
+IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+{
+ auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ return createNamedObjectsPerComponent(componentObjectIndex);
+ return *it;
}
+#endif // V4_BOOTSTRAP
-}
+} // CompiledData namespace
+} // QV4 namespace
Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index c9e535c93f..f1afad4965 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -90,7 +90,11 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
const QString &qstr = strings.at(i);
QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData);
+ s->refcount = -1;
s->size = qstr.length();
+ s->allocAndCapacityReservedFlag = 0;
+ s->offsetOn32Bit = sizeof(QV4::CompiledData::String);
+ s->offsetOn64Bit = sizeof(QV4::CompiledData::String);
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort));
#else
@@ -98,6 +102,7 @@ void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
for (int i = 0; i < qstr.length(); ++i)
uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode());
#endif
+ reinterpret_cast<ushort *>(s + 1)[s->size] = 0;
stringData += QV4::CompiledData::String::calculateSize(qstr);
}
@@ -183,7 +188,7 @@ QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx)
return constants.at(idx);
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members)
+int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
{
// ### re-use existing class definitions.
@@ -197,31 +202,15 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &m
jsClass->nMembers = members.size();
CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- for (const MemberInfo &memberInfo : members) {
- member->nameOffset = registerString(memberInfo.name);
- member->isAccessor = memberInfo.isAccessor;
+ for (const auto &name : members) {
+ member->nameOffset = registerString(name);
+ member->isAccessor = false;
++member;
}
return jsClassOffsets.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSClassMember *members)
-{
- const int size = CompiledData::JSClass::calculateSize(count);
- jsClassOffsets.append(jsClassData.size());
- const int oldSize = jsClassData.size();
- jsClassData.resize(jsClassData.size() + size);
- memset(jsClassData.data() + oldSize, 0, size);
-
- CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
- jsClass->nMembers = count;
- CompiledData::JSClassMember *jsClassMembers = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- memcpy(jsClassMembers, members, sizeof(CompiledData::JSClassMember)*count);
-
- return jsClassOffsets.size() - 1;
-}
-
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
{
registerString(module->fileName);
@@ -233,28 +222,46 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
for (int i = 0; i < f->locals.size(); ++i)
registerString(f->locals.at(i));
}
+ for (Context *c : qAsConst(module->blocks)) {
+ for (int i = 0; i < c->locals.size(); ++i)
+ registerString(c->locals.at(i));
+ }
- Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le));
+ Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le));
uint jsClassDataOffset = 0;
char *dataPtr;
CompiledData::Unit *unit;
{
- QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, blockClassAndFunctionOffsets, &jsClassDataOffset);
dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
memset(dataPtr, 0, tempHeader.unitSize);
memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
memcpy(unit, &tempHeader, sizeof(tempHeader));
}
- memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->blockTableSize * sizeof(quint32_le));
for (int i = 0; i < module->functions.size(); ++i) {
Context *function = module->functions.at(i);
if (function == module->rootContext)
unit->indexOfRootFunction = i;
- writeFunction(dataPtr + functionOffsets[i], function);
+ writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function);
+ }
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+
+ writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
+ }
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *block = module->blocks.at(i);
+
+ writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->functions.size()], block);
}
CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
@@ -300,17 +307,16 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->nameIndex = getStringId(irFunction->name);
function->flags = 0;
- if (irFunction->hasDirectEval)
- function->flags |= CompiledData::Function::HasDirectEval;
- if (irFunction->usesArgumentsObject)
- function->flags |= CompiledData::Function::UsesArgumentsObject;
if (irFunction->isStrict)
function->flags |= CompiledData::Function::IsStrict;
- if (irFunction->hasTry || irFunction->hasWith)
- function->flags |= CompiledData::Function::HasCatchOrWith;
+ if (irFunction->isArrowFunction)
+ function->flags |= CompiledData::Function::IsArrowFunction;
+ if (irFunction->isGenerator)
+ function->flags |= CompiledData::Function::IsGenerator;
function->nestedFunctionIndex =
irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first()))
: std::numeric_limits<uint32_t>::max();
+ function->length = irFunction->formals ? irFunction->formals->length() : 0;
function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
@@ -324,7 +330,7 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine);
- function->nRegisters = irFunction->registerCount;
+ function->nRegisters = irFunction->registerCountInFunction;
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
@@ -390,7 +396,84 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
}
-QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset)
+static_assert(int(QV4::Compiler::Class::Method::Regular) == int(QV4::CompiledData::Method::Regular), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Getter) == int(QV4::CompiledData::Method::Getter), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Setter) == int(QV4::CompiledData::Method::Setter), "Incompatible layout");
+
+void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c)
+{
+ QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b);
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::Class);
+
+ QVector<Class::Method> allMethods = c.staticMethods;
+ allMethods += c.methods;
+
+ cls->constructorFunction = c.constructorIndex;
+ cls->nameIndex = c.nameIndex;
+ cls->nMethods = c.methods.size();
+ cls->nStaticMethods = c.staticMethods.size();
+ cls->methodTableOffset = currentOffset;
+ CompiledData::Method *method = reinterpret_cast<CompiledData::Method *>(b + currentOffset);
+
+ // write methods
+ for (int i = 0; i < allMethods.size(); ++i) {
+ method->name = allMethods.at(i).nameIndex;
+ method->type = allMethods.at(i).type;
+ method->function = allMethods.at(i).functionIndex;
+ ++method;
+ }
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Class " << stringForIndex(cls->nameIndex) << "static methods" << cls->nStaticMethods << "methods" << cls->nMethods;
+ qDebug() << " constructor:" << cls->constructorFunction;
+ const char *staticString = ": static ";
+ for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
+ if (i == cls->nStaticMethods)
+ staticString = ": ";
+ const char *type;
+ switch (cls->methodTable()[i].type) {
+ case CompiledData::Method::Getter:
+ type = "get "; break;
+ case CompiledData::Method::Setter:
+ type = "set "; break;
+ default:
+ type = "";
+
+ }
+ qDebug() << " " << i << staticString << type << stringForIndex(cls->methodTable()[i].name) << cls->methodTable()[i].function;
+ }
+ qDebug();
+ }
+}
+
+void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
+{
+ QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::Block);
+ currentOffset = (currentOffset + 7) & ~quint32(0x7);
+
+ block->nLocals = irBlock->locals.size();
+ block->localsOffset = currentOffset;
+ currentOffset += block->nLocals * sizeof(quint32);
+
+ // write locals
+ quint32_le *locals = (quint32_le *)(b + block->localsOffset);
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ locals[i] = getStringId(irBlock->locals.at(i));
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Variables for block" << irBlock->blockIndex;
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ qDebug() << " " << i << ":" << locals[i];
+ qDebug();
+ }
+}
+
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset)
{
CompiledData::Unit unit;
memset(&unit, 0, sizeof(unit));
@@ -409,6 +492,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.offsetToFunctionTable = nextOffset;
nextOffset += unit.functionTableSize * sizeof(uint);
+ unit.classTableSize = module->classes.size();
+ unit.offsetToClassTable = nextOffset;
+ nextOffset += unit.classTableSize * sizeof(uint);
+
+ unit.blockTableSize = module->blocks.size();
+ unit.offsetToBlockTable = nextOffset;
+ nextOffset += unit.blockTableSize * sizeof(uint);
+
unit.lookupTableSize = lookups.count();
unit.offsetToLookupTable = nextOffset;
nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
@@ -435,13 +526,29 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
for (int i = 0; i < module->functions.size(); ++i) {
Context *f = module->functions.at(i);
- functionOffsets[i] = nextOffset;
+ blockAndFunctionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
qmlIdDepsCount, qmlPropertyDepsCount, f->code.size());
}
+ blockAndFunctionOffsets += module->functions.size();
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Class::calculateSize(c.staticMethods.size(), c.methods.size());
+ }
+ blockAndFunctionOffsets += module->classes.size();
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *c = module->blocks.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Block::calculateSize(c->locals.size());
+ }
if (option == GenerateWithStringTable) {
unit.stringTableSize = stringTable.stringCount();
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 360af6540f..944c44b1ff 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -72,6 +72,8 @@ struct JSClassMember;
namespace Compiler {
+struct Class;
+
struct Q_QML_PRIVATE_EXPORT StringTableGenerator {
StringTableGenerator();
@@ -116,8 +118,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
int registerConstant(ReturnedValue v);
ReturnedValue constant(int idx);
- int registerJSClass(const QVector<MemberInfo> &members);
- int registerJSClass(int count, CompiledData::JSClassMember *members);
+ int registerJSClass(const QStringList &members);
enum GeneratorOption {
GenerateWithStringTable,
@@ -125,8 +126,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
};
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
- // Returns bytes written
void writeFunction(char *f, Context *irFunction) const;
+ void writeClass(char *f, const Class &c);
+ void writeBlock(char *f, Context *irBlock) const;
StringTableGenerator stringTable;
QString codeGeneratorName;
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 0a9d3d8efe..9dfe3be7e0 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -39,6 +39,7 @@
#include "qv4compilercontext_p.h"
#include "qv4compilercontrolflow_p.h"
+#include "qv4bytecodegenerator_p.h"
QT_USE_NAMESPACE
using namespace QV4;
@@ -47,11 +48,11 @@ using namespace QQmlJS::AST;
QT_BEGIN_NAMESPACE
-Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode)
+Context *Module::newContext(Node *node, Context *parent, ContextType contextType)
{
Q_ASSERT(!contextMap.contains(node));
- Context *c = new Context(parent, compilationMode);
+ Context *c = new Context(parent, contextType);
if (node) {
SourceLocation loc = node->firstSourceLocation();
c->line = loc.startLine;
@@ -70,15 +71,252 @@ Context *Module::newContext(Node *node, Context *parent, CompilationMode compila
return c;
}
-bool Context::forceLookupByName()
+bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function)
{
- ControlFlow *flow = controlFlow;
- while (flow) {
- if (flow->needsLookupByName)
+ // ### can this happen?
+ if (name.isEmpty())
+ return true;
+
+ if (type != FunctionDefinition) {
+ if (formals && formals->containsName(name))
+ return (scope == VariableScope::Var);
+ }
+ if (!isCatchBlock || name != caughtVariable) {
+ MemberMap::iterator it = members.find(name);
+ if (it != members.end()) {
+ if (scope != VariableScope::Var || (*it).scope != VariableScope::Var)
+ return false;
+ if ((*it).type <= type) {
+ (*it).type = type;
+ (*it).function = function;
+ }
return true;
- flow = flow->parent;
+ }
+ }
+
+ // hoist var declarations to the function level
+ if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
+ return parent->addLocalVar(name, type, scope, function);
+
+ Member m;
+ m.type = type;
+ m.function = function;
+ m.scope = scope;
+ members.insert(name, m);
+ return true;
+}
+
+Context::ResolvedName Context::resolveName(const QString &name)
+{
+ int scope = 0;
+ Context *c = this;
+
+ ResolvedName result;
+
+ while (c) {
+ if (c->isWithBlock)
+ return result;
+
+ Context::Member m = c->findMember(name);
+ if (!c->parent && m.index < 0)
+ break;
+
+ if (m.type != Context::UndefinedMember) {
+ result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack;
+ result.scope = scope;
+ result.index = m.index;
+ result.isConst = (m.scope == VariableScope::Const);
+ if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
+ result.isArgOrEval = true;
+ return result;
+ }
+ const int argIdx = c->findArgument(name);
+ if (argIdx != -1) {
+ if (c->argumentsCanEscape) {
+ result.index = argIdx + c->locals.size();
+ result.scope = scope;
+ result.type = ResolvedName::Local;
+ result.isConst = false;
+ return result;
+ } else {
+ result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1;
+ result.scope = 0;
+ result.type = ResolvedName::Stack;
+ result.isConst = false;
+ return result;
+ }
+ }
+ if (c->hasDirectEval) {
+ Q_ASSERT(!c->isStrict && c->contextType != ContextType::Block);
+ return result;
+ }
+
+ if (c->requiresExecutionContext)
+ ++scope;
+ c = c->parent;
+ }
+
+ // ### can we relax the restrictions here?
+ if (contextType == ContextType::Eval || c->contextType == ContextType::Binding)
+ return result;
+
+ result.type = ResolvedName::Global;
+ return result;
+}
+
+void Context::emitBlockHeader(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ setupFunctionIndices(bytecodeGenerator);
+
+ if (requiresExecutionContext) {
+ if (blockIndex < 0) {
+ codegen->module()->blocks.append(this);
+ blockIndex = codegen->module()->blocks.count() - 1;
+ }
+
+ if (contextType == ContextType::Global) {
+ Instruction::PushScriptContext scriptContext;
+ scriptContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(scriptContext);
+ } else if (contextType == ContextType::Block || (contextType == ContextType::Eval && !isStrict)) {
+ if (isCatchBlock) {
+ Instruction::PushCatchContext catchContext;
+ catchContext.index = blockIndex;
+ catchContext.name = codegen->registerString(caughtVariable);
+ bytecodeGenerator->addInstruction(catchContext);
+ } else {
+ Instruction::PushBlockContext blockContext;
+ blockContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(blockContext);
+ }
+ } else {
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
+ }
+
+ if (usesThis) {
+ Q_ASSERT(!isStrict);
+ // make sure we convert this to an object
+ Instruction::ConvertThisToObject convert;
+ bytecodeGenerator->addInstruction(convert);
+ }
+
+ if (contextType == ContextType::Global || (contextType == ContextType::Eval && !isStrict)) {
+ // variables in global code are properties of the global context object, not locals as with other functions.
+ for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
+ if (it->isLexicallyScoped())
+ continue;
+ const QString &local = it.key();
+
+ Instruction::DeclareVar declareVar;
+ declareVar.isDeletable = (contextType == ContextType::Eval);
+ declareVar.varName = codegen->registerString(local);
+ bytecodeGenerator->addInstruction(declareVar);
+ }
+ }
+
+ if (contextType == ContextType::Function || contextType == ContextType::Binding) {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (it->canEscape && it->type == Context::ThisFunctionName) {
+ // move the function from the stack to the call context
+ Instruction::LoadReg load;
+ load.reg = CallData::Function;
+ bytecodeGenerator->addInstruction(load);
+ Instruction::StoreLocal store;
+ store.index = it->index;
+ bytecodeGenerator->addInstruction(store);
+ }
+ }
+ }
+
+ if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Q_ASSERT(contextType != ContextType::Block);
+ if (isStrict || (formals && !formals->isSimpleParameterList())) {
+ Instruction::CreateUnmappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ } else {
+ Instruction::CreateMappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ }
+ codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
+ }
+
+ for (const Context::Member &member : qAsConst(members)) {
+ if (member.function) {
+ const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body);
+ codegen->loadClosure(function);
+ Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true);
+ r.storeConsumeAccumulator();
+ }
+ }
+}
+
+void Context::emitBlockFooter(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ if (!requiresExecutionContext)
+ return;
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
+ if (contextType == ContextType::Global)
+ bytecodeGenerator->addInstruction(Instruction::PopScriptContext());
+ else
+ bytecodeGenerator->addInstruction(Instruction::PopContext());
+QT_WARNING_POP
+}
+
+void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
+{
+ if (registerOffset != -1) {
+ // already computed, check for consistency
+ Q_ASSERT(registerOffset == bytecodeGenerator->currentRegister());
+ bytecodeGenerator->newRegisterArray(nRegisters);
+ return;
+ }
+ Q_ASSERT(locals.size() == 0);
+ Q_ASSERT(nRegisters == 0);
+ registerOffset = bytecodeGenerator->currentRegister();
+
+ switch (contextType) {
+ case ContextType::Block:
+ case ContextType::Function:
+ case ContextType::Binding: {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ const QString &local = it.key();
+ if (it->canEscape) {
+ it->index = locals.size();
+ locals.append(local);
+ } else {
+ if (it->type == Context::ThisFunctionName)
+ it->index = CallData::Function;
+ else
+ it->index = bytecodeGenerator->newRegister();
+ }
+ }
+ break;
+ }
+ case ContextType::Global:
+ case ContextType::Eval:
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (!it->isLexicallyScoped() && (contextType == ContextType::Global || !isStrict))
+ continue;
+ if (it->canEscape) {
+ it->index = locals.size();
+ locals.append(it.key());
+ } else {
+ it->index = bytecodeGenerator->newRegister();
+ }
+ }
+ break;
}
- return false;
+ nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
}
QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 455a76c729..52c3fc5b05 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -66,18 +66,37 @@ namespace Compiler {
struct ControlFlow;
-enum CompilationMode {
- GlobalCode,
- EvalCode,
- FunctionCode,
- QmlBinding // This is almost the same as EvalCode, except:
+enum class ContextType {
+ Global,
+ Function,
+ Eval,
+ Binding, // This is almost the same as Eval, except:
// * function declarations are moved to the return address when encountered
// * return statements are allowed everywhere (like in FunctionCode)
// * variable declarations are treated as true locals (like in FunctionCode)
+ Block
};
struct Context;
+struct Class {
+ struct Method {
+ enum Type {
+ Regular,
+ Getter,
+ Setter
+ };
+ uint nameIndex;
+ Type type;
+ uint functionIndex;
+ };
+
+ uint nameIndex;
+ uint constructorIndex = UINT_MAX;
+ QVector<Method> staticMethods;
+ QVector<Method> methods;
+};
+
struct Module {
Module(bool debugMode)
: debugMode(debugMode)
@@ -86,10 +105,12 @@ struct Module {
qDeleteAll(contextMap);
}
- Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode);
+ Context *newContext(QQmlJS::AST::Node *node, Context *parent, ContextType compilationMode);
QHash<QQmlJS::AST::Node *, Context *> contextMap;
QList<Context *> functions;
+ QList<Context *> blocks;
+ QVector<Class> classes;
Context *rootContext;
QString fileName;
QString finalUrl;
@@ -104,8 +125,9 @@ struct Context {
QString name;
int line = 0;
int column = 0;
- int registerCount = 0;
+ int registerCountInFunction = 0;
int functionIndex = -1;
+ int blockIndex = -1;
enum MemberType {
UndefinedMember,
@@ -118,11 +140,11 @@ struct Context {
struct Member {
MemberType type = UndefinedMember;
int index = -1;
- QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope;
+ QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var;
mutable bool canEscape = false;
QQmlJS::AST::FunctionExpression *function = nullptr;
- bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; }
+ bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; }
};
typedef QMap<QString, Member> MemberMap;
@@ -137,15 +159,22 @@ struct Context {
QByteArray code;
QVector<CompiledData::CodeOffsetToLine> lineNumberMapping;
- int maxNumberOfArguments = 0;
+ int nRegisters = 0;
+ int registerOffset = -1;
bool hasDirectEval = false;
+ bool allVarsEscape = false;
bool hasNestedFunctions = false;
bool isStrict = false;
+ bool isArrowFunction = false;
+ bool isGenerator = false;
bool usesThis = false;
bool hasTry = false;
- bool hasWith = false;
bool returnsClosure = false;
mutable bool argumentsCanEscape = false;
+ bool requiresExecutionContext = false;
+ bool isWithBlock = false;
+ bool isCatchBlock = false;
+ QString caughtVariable;
enum UsesArgumentsObject {
ArgumentsObjectUnknown,
@@ -155,7 +184,7 @@ struct Context {
UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
- CompilationMode compilationMode;
+ ContextType contextType;
template <typename T>
class SmallSet: public QVarLengthArray<T, 8>
@@ -204,24 +233,14 @@ struct Context {
PropertyDependencyMap contextObjectPropertyDependencies;
PropertyDependencyMap scopeObjectPropertyDependencies;
- Context(Context *parent, CompilationMode mode)
+ Context(Context *parent, ContextType type)
: parent(parent)
- , compilationMode(mode)
+ , contextType(type)
{
if (parent && parent->isStrict)
isStrict = true;
}
- bool forceLookupByName();
-
-
- bool canUseSimpleCall() const {
- return nestedContexts.isEmpty() &&
- locals.isEmpty() &&
- !hasTry && !hasWith &&
- (usesArgumentsObject == ArgumentsObjectNotUsed || isStrict) && !hasDirectEval;
- }
-
int findArgument(const QString &name)
{
// search backwards to handle duplicate argument names correctly
@@ -253,37 +272,37 @@ struct Context {
return true;
}
+ bool requiresImplicitReturnValue() const {
+ return contextType == ContextType::Binding ||
+ contextType == ContextType::Eval ||
+ contextType == ContextType::Global;
+ }
+
void addUsedVariable(const QString &name) {
usedVariables.insert(name);
}
- bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr)
- {
- if (name.isEmpty())
- return true;
+ bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr);
+
+ struct ResolvedName {
+ enum Type {
+ Unresolved,
+ Global,
+ Local,
+ Stack
+ };
+ Type type = Unresolved;
+ bool isArgOrEval = false;
+ bool isConst = false;
+ int scope = -1;
+ int index = -1;
+ bool isValid() const { return type != Unresolved; }
+ };
+ ResolvedName resolveName(const QString &name);
+ void emitBlockHeader(Compiler::Codegen *codegen);
+ void emitBlockFooter(Compiler::Codegen *codegen);
- if (type != FunctionDefinition) {
- for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
- if (it->name == name)
- return (scope == QQmlJS::AST::VariableDeclaration::FunctionScope);
- }
- MemberMap::iterator it = members.find(name);
- if (it != members.end()) {
- if (scope != QQmlJS::AST::VariableDeclaration::FunctionScope || (*it).scope != QQmlJS::AST::VariableDeclaration::FunctionScope)
- return false;
- if ((*it).type <= type) {
- (*it).type = type;
- (*it).function = function;
- }
- return true;
- }
- Member m;
- m.type = type;
- m.function = function;
- m.scope = scope;
- members.insert(name, m);
- return true;
- }
+ void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator);
};
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index 9bda20905a..1ef290ea56 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -53,6 +53,7 @@
#include <private/qv4global_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qv4bytecodegenerator_p.h>
QT_BEGIN_NAMESPACE
@@ -68,117 +69,84 @@ struct ControlFlow {
enum Type {
Loop,
With,
+ Block,
Finally,
Catch
};
- enum HandlerType {
- Invalid,
+ enum UnwindType {
Break,
Continue,
- Return,
- Throw
+ Return
};
- struct Handler {
- HandlerType type;
- QString label;
+ struct UnwindTarget {
BytecodeGenerator::Label linkLabel;
- int tempIndex;
- int value;
+ int unwindLevel;
};
Codegen *cg;
ControlFlow *parent;
Type type;
- bool needsLookupByName = false;
ControlFlow(Codegen *cg, Type type)
- : cg(cg), parent(cg->_context->controlFlow), type(type)
+ : cg(cg), parent(cg->controlFlow), type(type)
{
- cg->_context->controlFlow = this;
+ cg->controlFlow = this;
}
virtual ~ControlFlow() {
- cg->_context->controlFlow = parent;
+ cg->controlFlow = parent;
}
- void emitReturnStatement() const {
- if (cg->_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(cg->_returnAddress);
- generator()->addInstruction(load);
+ UnwindTarget unwindTarget(UnwindType type, const QString &label = QString())
+ {
+ Q_ASSERT(type == Break || type == Continue || type == Return);
+ ControlFlow *flow = this;
+ int level = 0;
+ while (flow) {
+ BytecodeGenerator::Label l = flow->getUnwindTarget(type, label);
+ if (l.isValid())
+ return UnwindTarget{l, level};
+ if (flow->requiresUnwind())
+ ++level;
+ flow = flow->parent;
}
- Instruction::Ret ret;
- cg->bytecodeGenerator->addInstruction(ret);
+ if (type == Return)
+ return UnwindTarget{ cg->returnLabel(), level };
+ return UnwindTarget();
}
- void jumpToHandler(const Handler &h) {
- if (h.linkLabel.isReturn()) {
- emitReturnStatement();
- } else {
- if (h.tempIndex >= 0)
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- cg->bytecodeGenerator->jump().link(h.linkLabel);
- }
- }
+ virtual QString label() const { return QString(); }
- bool returnRequiresUnwind() const {
- const ControlFlow *f = this;
- while (f) {
- if (f->type == Finally)
+ bool hasLoop() const {
+ const ControlFlow *flow = this;
+ while (flow) {
+ if (flow->type == Loop)
return true;
- f = f->parent;
+ flow = flow->parent;
}
return false;
}
- virtual QString label() const { return QString(); }
-
- bool isSimple() const {
- return type == Loop;
+protected:
+ virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) {
+ return BytecodeGenerator::Label();
}
-
- Handler getParentHandler(HandlerType type, const QString &label = QString()) {
- if (parent)
- return parent->getHandler(type, label);
- switch (type) {
- case Break:
- case Continue:
- return { Invalid, QString(), {}, -1, 0 };
- case Return:
- case Throw:
- return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 };
- case Invalid:
- break;
- }
- Q_ASSERT(false);
- Q_UNREACHABLE();
+ virtual bool requiresUnwind() {
+ return false;
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0;
-
- BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
- return parent ? parent->exceptionHandler() : nullptr;
+public:
+ BytecodeGenerator::ExceptionHandler *parentUnwindHandler() {
+ return parent ? parent->unwindHandler() : nullptr;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return parentExceptionHandler();
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() {
+ return parentUnwindHandler();
}
- virtual void handleThrow(const Reference &expr) {
- Reference e = expr;
- Handler h = getHandler(ControlFlow::Throw);
- if (h.tempIndex >= 0) {
- e = e.storeOnStack();
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- }
- e.loadInAccumulator();
- Instruction::ThrowException instr;
- generator()->addInstruction(instr);
- }
-
protected:
QString loopLabel() const {
QString label;
@@ -193,136 +161,145 @@ protected:
}
};
-struct ControlFlowLoop : public ControlFlow
+struct ControlFlowUnwind : public ControlFlow
{
- QString loopLabel;
- BytecodeGenerator::Label *breakLabel = nullptr;
- BytecodeGenerator::Label *continueLabel = nullptr;
+ BytecodeGenerator::ExceptionHandler unwindLabel;
- ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr)
- : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
+ ControlFlowUnwind(Codegen *cg, Type type)
+ : ControlFlow(cg, type)
{
}
- virtual QString label() const { return loopLabel; }
+ void setupUnwindHandler()
+ {
+ unwindLabel = generator()->newExceptionHandler();
+ }
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- switch (type) {
- case Break:
- if (breakLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *breakLabel, -1, 0 };
- break;
- case Continue:
- if (continueLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *continueLabel, -1, 0 };
- break;
- case Return:
- case Throw:
- break;
- case Invalid:
- Q_ASSERT(false);
- Q_UNREACHABLE();
- }
- return getParentHandler(type, label);
+ void emitUnwindHandler()
+ {
+ Q_ASSERT(requiresUnwind());
+
+ Instruction::UnwindDispatch dispatch;
+ generator()->addInstruction(dispatch);
}
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler();
+ }
};
-struct ControlFlowUnwind : public ControlFlow
+struct ControlFlowLoop : public ControlFlowUnwind
{
- BytecodeGenerator::ExceptionHandler unwindLabel;
- int controlFlowTemp;
- QVector<Handler> handlers;
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = nullptr;
+ BytecodeGenerator::Label *continueLabel = nullptr;
+ bool _requiresUnwind;
- ControlFlowUnwind(Codegen *cg, Type type)
- : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler())
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, bool requiresUnwind = false)
+ : ControlFlowUnwind(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel), _requiresUnwind(requiresUnwind)
{
- Q_ASSERT(type != Loop);
- controlFlowTemp = static_cast<int>(generator()->newRegister());
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- // we'll need at least a handler for throw
- getHandler(Throw);
+ if (_requiresUnwind) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
}
- void emitUnwindHandler()
- {
- Q_ASSERT(!isSimple());
-
- Reference temp = Reference::fromStackSlot(cg, controlFlowTemp);
- for (const auto &h : qAsConst(handlers)) {
- Handler parentHandler = getParentHandler(h.type, h.label);
-
- if (h.type == Throw || parentHandler.tempIndex >= 0) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- if (h.type == Throw)
- emitForThrowHandling();
- jumpToHandler(parentHandler);
- skip.link();
- } else {
- if (parentHandler.linkLabel.isReturn()) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- emitReturnStatement();
- skip.link();
- } else {
- generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel);
- }
- }
+ ~ControlFlowLoop() {
+ if (_requiresUnwind) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ emitUnwindHandler();
}
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- for (const auto &h : qAsConst(handlers)) {
- if (h.type == type && h.label == label)
- return h;
- }
- Handler h = {
- type,
- label,
- unwindLabel,
- controlFlowTemp,
- handlers.size()
- };
- handlers.append(h);
- return h;
+ bool requiresUnwind() override {
+ return _requiresUnwind;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return &unwindLabel;
+ BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override {
+ switch (type) {
+ case Break:
+ if (breakLabel && (label.isEmpty() || label == loopLabel))
+ return *breakLabel;
+ break;
+ case Continue:
+ if (continueLabel && (label.isEmpty() || label == loopLabel))
+ return *continueLabel;
+ break;
+ default:
+ break;
+ }
+ return BytecodeGenerator::Label();
}
- virtual void emitForThrowHandling() { }
+ QString label() const override { return loopLabel; }
};
+
struct ControlFlowWith : public ControlFlowUnwind
{
ControlFlowWith(Codegen *cg)
: ControlFlowUnwind(cg, With)
{
- needsLookupByName = true;
-
- savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister());
+ setupUnwindHandler();
// assumes the with object is in the accumulator
Instruction::PushWithContext pushScope;
- pushScope.reg = savedContextRegister;
generator()->addInstruction(pushScope);
- generator()->setExceptionHandler(&unwindLabel);
+ generator()->setUnwindHandler(&unwindLabel);
}
- virtual ~ControlFlowWith() {
+ ~ControlFlowWith() {
// emit code for unwinding
unwindLabel.link();
- generator()->setExceptionHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
Instruction::PopContext pop;
- pop.reg = savedContextRegister;
generator()->addInstruction(pop);
emitUnwindHandler();
}
- Moth::StackSlot savedContextRegister;
+
+ bool requiresUnwind() override {
+ return true;
+ }
+
+
+};
+
+struct ControlFlowBlock : public ControlFlowUnwind
+{
+ ControlFlowBlock(Codegen *cg, AST::Node *ast)
+ : ControlFlowUnwind(cg, Block)
+ {
+ block = cg->enterBlock(ast);
+ block->emitBlockHeader(cg);
+
+ if (block->requiresExecutionContext) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
+ }
+
+ virtual ~ControlFlowBlock() {
+ // emit code for unwinding
+ if (block->requiresExecutionContext) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ }
+
+ block->emitBlockFooter(cg);
+
+ if (block->requiresExecutionContext )
+ emitUnwindHandler();
+ cg->leaveBlock();
+ }
+
+ virtual bool requiresUnwind() override {
+ return block->requiresExecutionContext;
+ }
+
+ Context *block;
};
struct ControlFlowCatch : public ControlFlowUnwind
@@ -330,71 +307,57 @@ struct ControlFlowCatch : public ControlFlowUnwind
AST::Catch *catchExpression;
bool insideCatch = false;
BytecodeGenerator::ExceptionHandler exceptionLabel;
- BytecodeGenerator::ExceptionHandler catchUnwindLabel;
+ bool oldLookupByName;
ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
: ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
- exceptionLabel(generator()->newExceptionHandler()),
- catchUnwindLabel(generator()->newExceptionHandler())
+ exceptionLabel(generator()->newExceptionHandler())
{
- generator()->setExceptionHandler(&exceptionLabel);
+ generator()->setUnwindHandler(&exceptionLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- Handler h = getParentHandler(type, label);
- if (h.type == Invalid)
- return h;
- h = ControlFlowUnwind::getHandler(type, label);
- if (insideCatch)
- // if we're inside the catch block, we need to jump to the pop scope
- // instruction at the end of the catch block, not the unwind handler
- h.linkLabel = catchUnwindLabel;
- else if (type == Throw)
- // if we're inside the try block, we need to jump to the catch block,
- // not the unwind handler
- h.linkLabel = exceptionLabel;
- return h;
+ virtual bool requiresUnwind() override {
+ return true;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideCatch ? &catchUnwindLabel : &exceptionLabel;
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideCatch ? &unwindLabel : &exceptionLabel;
}
~ControlFlowCatch() {
// emit code for unwinding
-
- needsLookupByName = true;
insideCatch = true;
+ setupUnwindHandler();
Codegen::RegisterScope scope(cg);
// exceptions inside the try block go here
exceptionLabel.link();
- Moth::StackSlot savedContextReg = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::PushCatchContext pushCatch;
- pushCatch.name = cg->registerString(catchExpression->name.toString());
- pushCatch.reg = savedContextReg;
- generator()->addInstruction(pushCatch);
- // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- generator()->setExceptionHandler(&catchUnwindLabel);
+ BytecodeGenerator::Jump noException = generator()->jumpNoException();
- cg->statement(catchExpression->statement);
+ Context *block = cg->enterBlock(catchExpression);
- insideCatch = false;
- needsLookupByName = false;
+ block->emitBlockHeader(cg);
- // exceptions inside catch and break/return statements go here
- catchUnwindLabel.link();
- Instruction::PopContext pop;
- pop.reg = savedContextReg;
- generator()->addInstruction(pop);
+ generator()->setUnwindHandler(&unwindLabel);
+
+ if (catchExpression->patternElement->bindingIdentifier.isEmpty())
+ // destructuring pattern
+ cg->initializeAndDestructureBindingElement(catchExpression->patternElement, Reference::fromName(cg, QStringLiteral("@caught")));
+ // skip the additional block
+ cg->statementList(catchExpression->statement->statements);
- // break/continue/return statements in try go here
+ // exceptions inside catch and break/return statements go here
unwindLabel.link();
- generator()->setExceptionHandler(parentExceptionHandler());
+ block->emitBlockFooter(cg);
+
+ cg->leaveBlock();
+
+ noException.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
emitUnwindHandler();
+ insideCatch = false;
}
};
@@ -402,25 +365,21 @@ struct ControlFlowFinally : public ControlFlowUnwind
{
AST::Finally *finally;
bool insideFinally = false;
- int exceptionTemp = -1;
ControlFlowFinally(Codegen *cg, AST::Finally *finally)
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
- generator()->setExceptionHandler(&unwindLabel);
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- // if we're inside the finally block, any exceptions etc. should
- // go directly to the parent handler
- if (insideFinally)
- return getParentHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
+ virtual bool requiresUnwind() override {
+ return !insideFinally;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler();
}
~ControlFlowFinally() {
@@ -429,34 +388,35 @@ struct ControlFlowFinally : public ControlFlowUnwind
Codegen::RegisterScope scope(cg);
- Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::StoreReg storeRetVal;
- storeRetVal.reg = retVal;
- generator()->addInstruction(storeRetVal);
-
insideFinally = true;
- exceptionTemp = generator()->newRegister();
+ int returnValueTemp = -1;
+ if (cg->requiresReturnValue) {
+ returnValueTemp = generator()->newRegister();
+ Instruction::MoveReg move;
+ move.srcReg = cg->_returnAddress;
+ move.destReg = returnValueTemp;
+ generator()->addInstruction(move);
+ }
+ int exceptionTemp = generator()->newRegister();
Instruction::GetException instr;
generator()->addInstruction(instr);
Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator();
- generator()->setExceptionHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
cg->statement(finally->statement);
insideFinally = false;
- Instruction::LoadReg loadRetVal;
- loadRetVal.reg = retVal;
- generator()->addInstruction(loadRetVal);
-
- emitUnwindHandler();
- }
-
- virtual void emitForThrowHandling() {
- // reset the exception flag, that got cleared before executing the statements in finally
+ if (cg->requiresReturnValue) {
+ Instruction::MoveReg move;
+ move.srcReg = returnValueTemp;
+ move.destReg = cg->_returnAddress;
+ generator()->addInstruction(move);
+ }
Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator();
Instruction::SetException setException;
- Q_ASSERT(exceptionTemp != -1);
generator()->addInstruction(setException);
+
+ emitUnwindHandler();
}
};
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 84ee452332..9be55c6ad0 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -56,12 +56,12 @@ using namespace QV4;
using namespace QV4::Compiler;
using namespace QQmlJS::AST;
-ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
+ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
: _cg(cg)
, _sourceCode(sourceCode)
, _context(nullptr)
, _allowFuncDecls(true)
- , defaultProgramMode(defaultProgramMode)
+ , defaultProgramType(defaultProgramType)
{
}
@@ -73,18 +73,19 @@ void ScanFunctions::operator()(Node *node)
calcEscapingVariables();
}
-void ScanFunctions::enterGlobalEnvironment(CompilationMode compilationMode)
+void ScanFunctions::enterGlobalEnvironment(ContextType compilationMode)
{
- enterEnvironment(astNodeForGlobalEnvironment, compilationMode);
+ enterEnvironment(astNodeForGlobalEnvironment, compilationMode, QStringLiteral("%GlobalCode"));
}
-void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode)
+void ScanFunctions::enterEnvironment(Node *node, ContextType compilationMode, const QString &name)
{
Context *c = _cg->_module->contextMap.value(node);
if (!c)
c = _cg->_module->newContext(node, _context, compilationMode);
if (!c->isStrict)
c->isStrict = _cg->_strictMode;
+ c->name = name;
_contextStack.append(c);
_context = c;
}
@@ -92,28 +93,26 @@ void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode
void ScanFunctions::leaveEnvironment()
{
_contextStack.pop();
- _context = _contextStack.isEmpty() ? 0 : _contextStack.top();
-}
-
-void ScanFunctions::checkDirectivePrologue(SourceElements *ast)
-{
- for (SourceElements *it = ast; it; it = it->next) {
- if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) {
- if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) {
- if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
- // Use the source code, because the StringLiteral's
- // value might have escape sequences in it, which is not
- // allowed.
- if (strLit->literalToken.length < 2)
- continue;
- QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
- if (str == QLatin1String("use strict")) {
- _context->isStrict = true;
- } else {
- // TODO: give a warning.
- }
+ _context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
+}
+
+void ScanFunctions::checkDirectivePrologue(StatementList *ast)
+{
+ for (StatementList *it = ast; it; it = it->next) {
+ if (ExpressionStatement *expr = cast<ExpressionStatement *>(it->statement)) {
+ if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
+ // Use the source code, because the StringLiteral's
+ // value might have escape sequences in it, which is not
+ // allowed.
+ if (strLit->literalToken.length < 2)
continue;
+ QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
+ if (str == QLatin1String("use strict")) {
+ _context->isStrict = true;
+ } else {
+ // TODO: give a warning.
}
+ continue;
}
}
@@ -138,20 +137,10 @@ void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc)
}
}
-bool ScanFunctions::formalsContainName(AST::FormalParameterList *parameters, const QString &name)
-{
- while (parameters) {
- if (parameters->name == name)
- return true;
- parameters = parameters->next;
- }
- return false;
-}
-
bool ScanFunctions::visit(Program *ast)
{
- enterEnvironment(ast, defaultProgramMode);
- checkDirectivePrologue(ast->elements);
+ enterEnvironment(ast, defaultProgramType, QStringLiteral("%ProgramCode"));
+ checkDirectivePrologue(ast->statements);
return true;
}
@@ -162,7 +151,7 @@ void ScanFunctions::endVisit(Program *)
bool ScanFunctions::visit(CallExpression *ast)
{
- if (! _context->hasDirectEval) {
+ if (!_context->hasDirectEval) {
if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
if (id->name == QLatin1String("eval")) {
if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
@@ -171,53 +160,31 @@ bool ScanFunctions::visit(CallExpression *ast)
}
}
}
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
return true;
}
-bool ScanFunctions::visit(NewMemberExpression *ast)
+bool ScanFunctions::visit(PatternElement *ast)
{
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
- return true;
-}
+ if (!ast->isVariableDeclaration())
+ return true;
-bool ScanFunctions::visit(ArrayLiteral *ast)
-{
- int index = 0;
- for (ElementList *it = ast->elements; it; it = it->next) {
- for (Elision *elision = it->elision; elision; elision = elision->next)
- ++index;
- ++index;
- }
- if (ast->elision) {
- for (Elision *elision = ast->elision->next; elision; elision = elision->next)
- ++index;
- }
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index);
- return true;
-}
+ QStringList names;
+ ast->boundNames(&names);
-bool ScanFunctions::visit(VariableDeclaration *ast)
-{
- if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- checkName(ast->name, ast->identifierToken);
- if (ast->name == QLatin1String("arguments"))
- _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
- if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
- return false;
- }
- QString name = ast->name.toString();
- if (!_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
- return false;
+ for (const QString &name : qAsConst(names)) {
+ if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ checkName(QStringRef(&name), ast->identifierToken);
+ if (name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (ast->scope == VariableScope::Const && !ast->initializer && !ast->destructuringPattern()) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
+ return false;
+ }
+ if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
+ return false;
+ }
}
return true;
}
@@ -237,7 +204,8 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
if (!_allowFuncDecls)
_cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
- enterFunction(expr, /*enterName*/ true);
+ if (!enterFunction(expr, /*enterName*/ true))
+ return false;
Node::accept(expr->formals, this);
Node::accept(expr->body, this);
leaveEnvironment();
@@ -253,79 +221,98 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
bool ScanFunctions::visit(FunctionExpression *ast)
{
- enterFunction(ast, /*enterName*/ false);
+ return enterFunction(ast, /*enterName*/ false);
+}
+
+bool ScanFunctions::visit(ClassExpression *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
return true;
}
-void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
+void ScanFunctions::endVisit(ClassExpression *)
{
- if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
- enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : nullptr);
+ leaveEnvironment();
}
-void ScanFunctions::endVisit(FunctionExpression *)
+bool ScanFunctions::visit(ClassDeclaration *ast)
+{
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let);
+
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
+ return true;
+}
+
+void ScanFunctions::endVisit(ClassDeclaration *)
{
leaveEnvironment();
}
-bool ScanFunctions::visit(ObjectLiteral *ast)
+bool ScanFunctions::visit(TemplateLiteral *ast)
{
- int argc = 0;
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString key = it->assignment->name->asString();
- if (QV4::String::toArrayIndex(key) != UINT_MAX)
- ++argc;
- ++argc;
- if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
- ++argc;
+ while (ast) {
+ if (ast->expression)
+ Node::accept(ast->expression, this);
+ ast = ast->next;
}
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
+ return true;
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- Node::accept(ast->properties, this);
- return false;
}
-bool ScanFunctions::visit(PropertyGetterSetter *ast)
+bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
{
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/nullptr);
- return true;
+ if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
+ return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName);
}
-void ScanFunctions::endVisit(PropertyGetterSetter *)
+void ScanFunctions::endVisit(FunctionExpression *)
{
leaveEnvironment();
}
-bool ScanFunctions::visit(FunctionDeclaration *ast)
+bool ScanFunctions::visit(ObjectPattern *ast)
{
- enterFunction(ast, /*enterName*/ true);
- return true;
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ Node::accept(ast->properties, this);
+ return false;
}
-void ScanFunctions::endVisit(FunctionDeclaration *)
+bool ScanFunctions::visit(PatternProperty *ast)
{
- leaveEnvironment();
+ Q_UNUSED(ast);
+ // ### Shouldn't be required anymore
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) {
+// TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+// return enterFunction(ast, QString(), ast->formals, ast->functionBody, /*enterName */ false);
+// }
+ return true;
}
-bool ScanFunctions::visit(TryStatement *)
+void ScanFunctions::endVisit(PatternProperty *)
{
- // ### should limit to catch(), as try{} finally{} should be ok without
- _context->hasTry = true;
- return true;
+ // ###
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter)
+// leaveEnvironment();
}
-bool ScanFunctions::visit(WithStatement *ast)
+bool ScanFunctions::visit(FunctionDeclaration *ast)
{
- if (_context->isStrict) {
- _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
- return false;
- }
+ return enterFunction(ast, /*enterName*/ true);
+}
- _context->hasWith = true;
- return true;
+void ScanFunctions::endVisit(FunctionDeclaration *)
+{
+ leaveEnvironment();
}
bool ScanFunctions::visit(DoWhileStatement *ast) {
@@ -338,7 +325,9 @@ bool ScanFunctions::visit(DoWhileStatement *ast) {
}
bool ScanFunctions::visit(ForStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%For"));
Node::accept(ast->initialiser, this);
+ Node::accept(ast->declarations, this);
Node::accept(ast->condition, this);
Node::accept(ast->expression, this);
@@ -348,9 +337,14 @@ bool ScanFunctions::visit(ForStatement *ast) {
return false;
}
-bool ScanFunctions::visit(LocalForStatement *ast) {
- Node::accept(ast->declarations, this);
- Node::accept(ast->condition, this);
+void ScanFunctions::endVisit(ForStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ForEachStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach"));
+ Node::accept(ast->lhs, this);
Node::accept(ast->expression, this);
TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
@@ -359,90 +353,154 @@ bool ScanFunctions::visit(LocalForStatement *ast) {
return false;
}
-bool ScanFunctions::visit(ForEachStatement *ast) {
- Node::accept(ast->initialiser, this);
- Node::accept(ast->expression, this);
+void ScanFunctions::endVisit(ForEachStatement *)
+{
+ leaveEnvironment();
+}
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
- Node::accept(ast->statement, this);
+bool ScanFunctions::visit(ThisExpression *)
+{
+ _context->usesThis = true;
+ return false;
+}
+bool ScanFunctions::visit(Block *ast)
+{
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Block"));
+ Node::accept(ast->statements, this);
return false;
}
-bool ScanFunctions::visit(LocalForEachStatement *ast) {
- Node::accept(ast->declaration, this);
- Node::accept(ast->expression, this);
+void ScanFunctions::endVisit(Block *)
+{
+ leaveEnvironment();
+}
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
- Node::accept(ast->statement, this);
+bool ScanFunctions::visit(CaseBlock *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CaseBlock"));
+ return true;
+}
- return false;
+void ScanFunctions::endVisit(CaseBlock *)
+{
+ leaveEnvironment();
}
-bool ScanFunctions::visit(ThisExpression *)
+bool ScanFunctions::visit(Catch *ast)
{
- _context->usesThis = true;
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CatchBlock"));
+ _context->isCatchBlock = true;
+ QString caughtVar = ast->patternElement->bindingIdentifier.toString();
+ if (caughtVar.isEmpty())
+ caughtVar = QStringLiteral("@caught");
+ _context->addLocalVar(caughtVar, Context::MemberType::VariableDefinition, VariableScope::Let);
+
+ _context->caughtVariable = caughtVar;
+ if (_context->isStrict &&
+ (caughtVar == QLatin1String("eval") || caughtVar == QLatin1String("arguments"))) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
+ return false;
+ }
+ Node::accept(ast->patternElement, this);
+ // skip the block statement
+ Node::accept(ast->statement->statements, this);
return false;
}
-bool ScanFunctions::visit(Block *ast) {
+void ScanFunctions::endVisit(Catch *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(WithStatement *ast)
+{
+ Node::accept(ast->expression, this);
+
TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
- Node::accept(ast->statements, this);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%WithBlock"));
+ _context->isWithBlock = true;
+
+ if (_context->isStrict) {
+ _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
+ return false;
+ }
+ Node::accept(ast->statement, this);
+
return false;
}
-void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr)
+void ScanFunctions::endVisit(WithStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName)
{
Context *outerContext = _context;
- enterEnvironment(ast, FunctionCode);
+ enterEnvironment(ast, ContextType::Function, name);
+ FunctionExpression *expr = AST::cast<FunctionExpression *>(ast);
+ if (!expr)
+ expr = AST::cast<FunctionDeclaration *>(ast);
if (outerContext) {
outerContext->hasNestedFunctions = true;
// The identifier of a function expression cannot be referenced from the enclosing environment.
- if (expr) {
- if (!outerContext->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr)) {
+ if (enterName) {
+ if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr)) {
_cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
- return;
+ return false;
}
+ outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr);
}
if (name == QLatin1String("arguments"))
outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
}
- if (formalsContainName(formals, QStringLiteral("arguments")))
+ _context->name = name;
+ if (formals && formals->containsName(QStringLiteral("arguments")))
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (expr) {
+ if (expr->isArrowFunction)
+ _context->isArrowFunction = true;
+ else if (expr->isGenerator)
+ _context->isGenerator = true;
+ }
- if (!name.isEmpty() && !formalsContainName(formals, name))
- _context->addLocalVar(name, Context::ThisFunctionName, QQmlJS::AST::VariableDeclaration::FunctionScope);
+ if (!name.isEmpty() && (!formals || !formals->containsName(name)))
+ _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var);
_context->formals = formals;
if (body && !_context->isStrict)
- checkDirectivePrologue(body->elements);
-
- for (FormalParameterList *it = formals; it; it = it->next) {
- QString arg = it->name.toString();
- int duplicateIndex = _context->arguments.indexOf(arg);
- if (duplicateIndex != -1) {
- if (_context->isStrict) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg));
- return;
- } else {
- // change the name of the earlier argument to enforce the specified lookup semantics
- QString modified = arg;
- while (_context->arguments.contains(modified))
- modified += QString(0xfffe);
- _context->arguments[duplicateIndex] = modified;
+ checkDirectivePrologue(body);
+
+ bool isSimpleParameterList = formals && formals->isSimpleParameterList();
+
+ _context->arguments = formals ? formals->formals() : QStringList();
+
+ const QStringList boundNames = formals ? formals->boundNames() : QStringList();
+ for (int i = 0; i < boundNames.size(); ++i) {
+ const QString &arg = boundNames.at(i);
+ if (_context->isStrict || !isSimpleParameterList) {
+ bool duplicate = (boundNames.indexOf(arg, i + 1) != -1);
+ if (duplicate) {
+ _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg));
+ return false;
}
}
if (_context->isStrict) {
if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
- return;
+ _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
+ return false;
}
}
- _context->arguments += arg;
+ if (!_context->arguments.contains(arg))
+ _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var);
}
+ return true;
}
void ScanFunctions::calcEscapingVariables()
@@ -450,27 +508,114 @@ void ScanFunctions::calcEscapingVariables()
Module *m = _cg->_module;
for (Context *inner : qAsConst(m->contextMap)) {
+ if (inner->contextType == ContextType::Block && inner->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Context *c = inner->parent;
+ while (c->contextType == ContextType::Block)
+ c = c->parent;
+ c->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ }
+ }
+ for (Context *inner : qAsConst(m->contextMap)) {
+ if (!inner->parent || inner->usesArgumentsObject == Context::ArgumentsObjectUnknown)
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (inner->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ QString arguments = QStringLiteral("arguments");
+ inner->addLocalVar(arguments, Context::VariableDeclaration, AST::VariableScope::Var);
+ if (!inner->isStrict) {
+ inner->argumentsCanEscape = true;
+ inner->requiresExecutionContext = true;
+ }
+ }
+ }
+
+ for (Context *inner : qAsConst(m->contextMap)) {
for (const QString &var : qAsConst(inner->usedVariables)) {
Context *c = inner;
while (c) {
+ Context *current = c;
+ c = c->parent;
+ if (current->isWithBlock || current->contextType != ContextType::Block)
+ break;
+ }
+ Q_ASSERT(c != inner);
+ while (c) {
Context::MemberMap::const_iterator it = c->members.find(var);
if (it != c->members.end()) {
- if (c != inner)
+ if (c->parent || it->isLexicallyScoped()) {
it->canEscape = true;
+ c->requiresExecutionContext = true;
+ }
break;
}
if (c->findArgument(var) != -1) {
- if (c != inner)
- c->argumentsCanEscape = true;
+ c->argumentsCanEscape = true;
+ c->requiresExecutionContext = true;
break;
}
c = c->parent;
}
}
- Context *c = inner->parent;
- while (c) {
- c->hasDirectEval |= inner->hasDirectEval;
- c = c->parent;
+ if (inner->hasDirectEval) {
+ inner->hasDirectEval = false;
+ if (!inner->isStrict) {
+ Context *c = inner;
+ while (c->contextType == ContextType::Block) {
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ c->hasDirectEval = true;
+ }
+ Context *c = inner;
+ while (c) {
+ c->allVarsEscape = true;
+ c = c->parent;
+ }
+ }
+ if (inner->usesThis) {
+ inner->usesThis = false;
+ if (!inner->isStrict) {
+ Context *c = inner;
+ while (c->contextType == ContextType::Block) {
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ c->usesThis = true;
+ }
+ }
+ }
+ for (Context *c : qAsConst(m->contextMap)) {
+ if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
+ c->allVarsEscape = false;
+ if (c->contextType == ContextType::Global || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode)
+ c->allVarsEscape = true;
+ if (c->allVarsEscape) {
+ if (c->parent) {
+ c->requiresExecutionContext = true;
+ c->argumentsCanEscape = true;
+ } else {
+ for (const auto &m : qAsConst(c->members)) {
+ if (m.isLexicallyScoped()) {
+ c->requiresExecutionContext = true;
+ break;
+ }
+ }
+ }
+ }
+ if (c->contextType == ContextType::Block && c->isCatchBlock) {
+ c->requiresExecutionContext = true;
+ auto m = c->members.find(c->caughtVariable);
+ m->canEscape = true;
+ }
+ const QLatin1String exprForOn("expression for on");
+ if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() &&
+ c->name.startsWith(exprForOn) && c->name.at(exprForOn.size()).isUpper())
+ // we don't really need this for bindings, but we do for signal handlers, and in this case,
+ // we don't know if the code is a signal handler or not.
+ c->requiresExecutionContext = true;
+ if (c->allVarsEscape) {
+ for (auto &m : c->members)
+ m.canEscape = true;
}
}
@@ -478,10 +623,12 @@ void ScanFunctions::calcEscapingVariables()
if (showEscapingVars) {
qDebug() << "==== escaping variables ====";
for (Context *c : qAsConst(m->contextMap)) {
- qDebug() << "Context" << c->name << ":";
- qDebug() << " Arguments escape" << c->argumentsCanEscape;
+ qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict;
+ qDebug() << " parent:" << c->parent;
+ if (c->argumentsCanEscape)
+ qDebug() << " Arguments escape";
for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
- qDebug() << " " << it.key() << it.value().canEscape;
+ qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped();
}
}
}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 87b7210879..4c273600b3 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -58,6 +58,7 @@
#include <private/qv4util_p.h>
#include <QtCore/QStringList>
#include <QStack>
+#include <QScopedValueRollback>
QT_BEGIN_NAMESPACE
@@ -79,13 +80,13 @@ class Codegen;
class ScanFunctions: protected QQmlJS::AST::Visitor
{
- typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment;
+ typedef QScopedValueRollback<bool> TemporaryBoolAssignment;
public:
- ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode);
+ ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType);
void operator()(AST::Node *node);
- void enterGlobalEnvironment(CompilationMode compilationMode);
- void enterEnvironment(AST::Node *node, CompilationMode compilationMode);
+ void enterGlobalEnvironment(ContextType compilationMode);
+ void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name);
void leaveEnvironment();
void enterQmlFunction(AST::FunctionDeclaration *ast)
@@ -95,48 +96,60 @@ protected:
using Visitor::visit;
using Visitor::endVisit;
- void checkDirectivePrologue(AST::SourceElements *ast);
+ void checkDirectivePrologue(AST::StatementList *ast);
void checkName(const QStringRef &name, const AST::SourceLocation &loc);
- bool formalsContainName(AST::FormalParameterList *parameters, const QString &name);
bool visit(AST::Program *ast) override;
void endVisit(AST::Program *) override;
bool visit(AST::CallExpression *ast) override;
- bool visit(AST::NewMemberExpression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
+ bool visit(AST::PatternElement *ast) override;
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::ExpressionStatement *ast) override;
bool visit(AST::FunctionExpression *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
- void enterFunction(AST::FunctionExpression *ast, bool enterName);
+ bool enterFunction(AST::FunctionExpression *ast, bool enterName);
void endVisit(AST::FunctionExpression *) override;
- bool visit(AST::ObjectLiteral *ast) override;
+ bool visit(AST::ObjectPattern *ast) override;
- bool visit(AST::PropertyGetterSetter *ast) override;
- void endVisit(AST::PropertyGetterSetter *) override;
+ bool visit(AST::PatternProperty *ast) override;
+ void endVisit(AST::PatternProperty *) override;
bool visit(AST::FunctionDeclaration *ast) override;
void endVisit(AST::FunctionDeclaration *) override;
- bool visit(AST::TryStatement *ast) override;
- bool visit(AST::WithStatement *ast) override;
+ bool visit(AST::ClassExpression *ast) override;
+ void endVisit(AST::ClassExpression *) override;
+
+ bool visit(AST::ClassDeclaration *ast) override;
+ void endVisit(AST::ClassDeclaration *) override;
bool visit(AST::DoWhileStatement *ast) override;
bool visit(AST::ForStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
+ void endVisit(AST::ForStatement *) override;
bool visit(AST::ForEachStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
+ void endVisit(AST::ForEachStatement *) override;
+
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::Block *ast) override;
+ void endVisit(AST::Block *ast) override;
+
+ bool visit(AST::CaseBlock *ast) override;
+ void endVisit(AST::CaseBlock *ast) override;
+
+ bool visit(AST::Catch *ast) override;
+ void endVisit(AST::Catch *ast) override;
+
+ bool visit(AST::WithStatement *ast) override;
+ void endVisit(AST::WithStatement *ast) override;
protected:
- void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr);
+ bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
void calcEscapingVariables();
// fields:
@@ -146,7 +159,7 @@ protected:
QStack<Context *> _contextStack;
bool _allowFuncDecls;
- CompilationMode defaultProgramMode;
+ ContextType defaultProgramType;
private:
static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index 450fa50528..8e474b3783 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -39,15 +39,16 @@
#include "qv4instr_moth_p.h"
#include <private/qv4compileddata_p.h>
+#include <private/qv4stackframe_p.h>
using namespace QV4;
using namespace QV4::Moth;
int InstrInfo::size(Instr::Type type)
{
-#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: return InstrMeta<int(Instr::Type::I)>::Size;
+#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: case Instr::Type::I##_Wide: return InstrMeta<int(Instr::Type::I)>::Size;
switch (type) {
- FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_RETURN_INSTR_SIZE)
}
#undef MOTH_RETURN_INSTR_SIZE
Q_UNREACHABLE();
@@ -110,6 +111,8 @@ static QString toString(QV4::ReturnedValue v)
QDebug d = qDebug(); \
d.noquote(); \
d.nospace(); \
+ if (static_cast<int>(Instr::Type::instr) >= 0x100) \
+ --base_ptr; \
d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \
<< rawBytes(base_ptr, int(code - base_ptr)) << #instr << " ";
@@ -122,7 +125,7 @@ namespace QV4 {
namespace Moth {
const int InstrInfo::argumentCount[] = {
- FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS)
};
@@ -147,6 +150,8 @@ QString dumpRegister(int reg, int nFormals)
return QStringLiteral("(context)");
else if (reg == CallData::Accumulator)
return QStringLiteral("(accumulator)");
+ else if (reg == CallData::NewTarget)
+ return QStringLiteral("(new.target)");
else if (reg == CallData::This)
return QStringLiteral("(this)");
else if (reg == CallData::Argc)
@@ -286,10 +291,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(StoreNameStrict)
MOTH_BEGIN_INSTR(LoadElement)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
d << dumpRegister(base, nFormals) << "[acc]";
MOTH_END_INSTR(LoadElement)
@@ -298,20 +299,12 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
- d << dumpRegister(base, nFormals) << "[" << name << "]";
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
d << "acc[" << name << "]";
- MOTH_END_INSTR(LoadElementA)
+ MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
- d << dumpRegister(base, nFormals) << "(" << index << ")";
- MOTH_END_INSTR(GetLookup)
-
- MOTH_BEGIN_INSTR(GetLookupA)
d << "acc(" << index << ")";
- MOTH_END_INSTR(GetLookupA)
+ MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
d << dumpRegister(base, nFormals) << "[" << name<< "]";
@@ -321,6 +314,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "(" << index << ")";
MOTH_END_INSTR(SetLookup)
+ MOTH_BEGIN_INSTR(LoadSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(LoadSuperProperty)
+
+ MOTH_BEGIN_INSTR(StoreSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(StoreSuperProperty)
+
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
MOTH_END_INSTR(StoreScopeObjectProperty)
@@ -341,6 +342,13 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "[" << index << "]";
MOTH_END_INSTR(LoadIdObject)
+ MOTH_BEGIN_INSTR(Yield)
+ MOTH_END_INSTR(Yield)
+
+ MOTH_BEGIN_INSTR(Resume)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(Resume)
+
MOTH_BEGIN_INSTR(CallValue)
d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallValue)
@@ -377,12 +385,31 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallContextObjectProperty)
- MOTH_BEGIN_INSTR(SetExceptionHandler)
+ MOTH_BEGIN_INSTR(CallWithSpread)
+ d << "new" << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallWithSpread)
+
+ MOTH_BEGIN_INSTR(Construct)
+ d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(ConstructWithSpread)
+ d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(ConstructWithSpread)
+
+ MOTH_BEGIN_INSTR(SetUnwindHandler)
if (offset)
d << ABSOLUTE_OFFSET();
else
d << "<null>";
- MOTH_END_INSTR(SetExceptionHandler)
+ MOTH_END_INSTR(SetUnwindHandler)
+
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(UnwindToLabel)
MOTH_BEGIN_INSTR(ThrowException)
MOTH_END_INSTR(ThrowException)
@@ -397,30 +424,47 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushCatchContext)
- d << dumpRegister(reg, nFormals) << ", " << name;
+ d << index << ", " << name;
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(PushWithContext)
- d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ d << index;
+ MOTH_END_INSTR(PushBlockContext)
+
+ MOTH_BEGIN_INSTR(CloneBlockContext)
+ MOTH_END_INSTR(CloneBlockContext)
+
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ d << index;
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
- d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PopContext)
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
- MOTH_END_INSTR(ForeachIteratorObject)
+ MOTH_BEGIN_INSTR(GetIterator)
+ d << iterator;
+ MOTH_END_INSTR(GetIterator)
+
+ MOTH_BEGIN_INSTR(IteratorNext)
+ d << dumpRegister(value, nFormals);
+ MOTH_END_INSTR(IteratorNext)
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
- MOTH_END_INSTR(ForeachNextPropertyName)
+ MOTH_BEGIN_INSTR(IteratorClose)
+ d << dumpRegister(done, nFormals);
+ MOTH_END_INSTR(IteratorClose)
- MOTH_BEGIN_INSTR(DeleteMember)
- d << dumpRegister(base, nFormals) << "[" << member << "]";
- MOTH_END_INSTR(DeleteMember)
+ MOTH_BEGIN_INSTR(DestructureRestElement)
+ MOTH_END_INSTR(DestructureRestElement)
- MOTH_BEGIN_INSTR(DeleteSubscript)
+ MOTH_BEGIN_INSTR(DeleteProperty)
d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
- MOTH_END_INSTR(DeleteSubscript)
+ MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
d << name;
@@ -442,24 +486,35 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(DefineArray)
MOTH_BEGIN_INSTR(DefineObjectLiteral)
- d << dumpRegister(args, nFormals)
- << ", " << internalClassId
- << ", " << arrayValueCount
- << ", " << arrayGetterSetterCountAndFlags;
+ d << internalClassId
+ << ", " << argc
+ << ", " << dumpRegister(args, nFormals);
MOTH_END_INSTR(DefineObjectLiteral)
+ MOTH_BEGIN_INSTR(CreateClass)
+ d << classIndex
+ << ", " << dumpRegister(heritage, nFormals)
+ << ", " << dumpRegister(computedNames, nFormals);
+ MOTH_END_INSTR(CreateClass)
+
MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
MOTH_END_INSTR(CreateMappedArgumentsObject)
MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_BEGIN_INSTR(CreateRestParameter)
+ d << argIndex;
+ MOTH_END_INSTR(CreateRestParameter)
+
MOTH_BEGIN_INSTR(ConvertThisToObject)
MOTH_END_INSTR(ConvertThisToObject)
- MOTH_BEGIN_INSTR(Construct)
- d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
- MOTH_END_INSTR(Construct)
+ MOTH_BEGIN_INSTR(LoadSuperConstructor)
+ MOTH_END_INSTR(LoadSuperConstructor)
+
+ MOTH_BEGIN_INSTR(ToObject)
+ MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
d << ABSOLUTE_OFFSET();
@@ -473,6 +528,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNotUndefined)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNotUndefined)
+
+ MOTH_BEGIN_INSTR(JumpNoException)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNoException)
+
MOTH_BEGIN_INSTR(CmpEqNull)
MOTH_END_INSTR(CmpEqNull)
@@ -519,14 +582,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictNotEqual)
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
MOTH_END_INSTR(UNot)
@@ -597,6 +652,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << "acc, " << rhs;
MOTH_END_INSTR(ShlConst)
+ MOTH_BEGIN_INSTR(Exp)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Exp)
+
MOTH_BEGIN_INSTR(Mul)
d << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mul)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index df9182e924..ce92a31590 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -53,6 +53,8 @@
#include <private/qv4global_p.h>
#include <private/qv4value_p.h>
#include <private/qv4runtime_p.h>
+#include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper
+#include <qendian.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +62,7 @@ QT_BEGIN_NAMESPACE
op##_INSTRUCTION(name, nargs, __VA_ARGS__)
/* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */
+#define INSTR_Nop(op) INSTRUCTION(op, Nop, 0)
#define INSTR_Ret(op) INSTRUCTION(op, Ret, 0)
#define INSTR_Debug(op) INSTRUCTION(op, Debug, 0)
#define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index)
@@ -84,19 +87,20 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index)
#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, base)
-#define INSTR_LoadPropertyA(op) INSTRUCTION(op, LoadPropertyA, 1, name)
-#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base)
-#define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 1, index)
+#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name)
+#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index)
#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_Resume(op) INSTRUCTION(op, Resume, 1, offset)
#define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base)
#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, index)
-#define INSTR_LoadElementA(op) INSTRUCTION(op, LoadElementA, 1, base)
+#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base)
#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index)
#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv)
#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv)
@@ -107,32 +111,46 @@ QT_BEGIN_NAMESPACE
#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv)
#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv)
#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv)
-#define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset)
+#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv)
+#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
+#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
+#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0)
+#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset)
#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0)
-#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg)
-#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg)
-#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg)
-#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0)
-#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0)
-#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base)
-#define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index)
+#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, index, name)
+#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 0)
+#define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 1, index)
+#define INSTR_CloneBlockContext(op) INSTRUCTION(op, CloneBlockContext, 0)
+#define INSTR_PushScriptContext(op) INSTRUCTION(op, PushScriptContext, 1, index)
+#define INSTR_PopScriptContext(op) INSTRUCTION(op, PopScriptContext, 0)
+#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 0)
+#define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator)
+#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 1, value)
+#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 1, done)
+#define INSTR_DestructureRestElement(op) INSTRUCTION(op, DestructureRestElement, 0)
+#define INSTR_DeleteProperty(op) INSTRUCTION(op, DeleteProperty, 2, base, index)
#define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name)
#define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name)
#define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0)
#define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable)
#define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args)
-// arrayGetterSetterCountAndFlags contains 30 bits for count, 1 bit for needsSparseArray boolean
-#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 4, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags, args)
+#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 3, internalClassId, argc, args)
+#define INSTR_CreateClass(op) INSTRUCTION(op, CreateClass, 3, classIndex, heritage, computedNames)
#define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0)
#define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0)
+#define INSTR_CreateRestParameter(op) INSTRUCTION(op, CreateRestParameter, 1, argIndex)
#define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0)
-#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0)
+#define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0)
#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset)
#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset)
#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset)
+#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset)
+#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset)
#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs)
@@ -147,8 +165,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs)
#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
-#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset)
-#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset)
#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0)
@@ -168,6 +184,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs)
#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs)
#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs)
+#define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs)
#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs)
#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs)
@@ -175,10 +192,12 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
+#define FOR_EACH_MOTH_INSTR_ALL(F) \
+ F(Nop) \
+ FOR_EACH_MOTH_INSTR(F)
#define FOR_EACH_MOTH_INSTR(F) \
F(Ret) \
- F(Debug) \
F(LoadConst) \
F(LoadZero) \
F(LoadTrue) \
@@ -186,6 +205,7 @@ QT_BEGIN_NAMESPACE
F(LoadNull) \
F(LoadUndefined) \
F(LoadInt) \
+ F(LoadRuntimeString) \
F(MoveConst) \
F(LoadReg) \
F(StoreReg) \
@@ -194,7 +214,6 @@ QT_BEGIN_NAMESPACE
F(StoreLocal) \
F(LoadScopedLocal) \
F(StoreScopedLocal) \
- F(LoadRuntimeString) \
F(MoveRegExp) \
F(LoadClosure) \
F(LoadName) \
@@ -202,53 +221,25 @@ QT_BEGIN_NAMESPACE
F(StoreNameSloppy) \
F(StoreNameStrict) \
F(LoadElement) \
- F(LoadElementA) \
F(StoreElement) \
F(LoadProperty) \
- F(LoadPropertyA) \
F(GetLookup) \
- F(GetLookupA) \
F(StoreProperty) \
F(SetLookup) \
+ F(LoadSuperProperty) \
+ F(StoreSuperProperty) \
F(StoreScopeObjectProperty) \
F(StoreContextObjectProperty) \
F(LoadScopeObjectProperty) \
F(LoadContextObjectProperty) \
F(LoadIdObject) \
- F(CallValue) \
- F(CallProperty) \
- F(CallPropertyLookup) \
- F(CallElement) \
- F(CallName) \
- F(CallPossiblyDirectEval) \
- F(CallGlobalLookup) \
- F(CallScopeObjectProperty) \
- F(CallContextObjectProperty) \
- F(SetExceptionHandler) \
- F(ThrowException) \
- F(GetException) \
- F(SetException) \
- F(CreateCallContext) \
- F(PushCatchContext) \
- F(PushWithContext) \
- F(PopContext) \
- F(ForeachIteratorObject) \
- F(ForeachNextPropertyName) \
- F(DeleteMember) \
- F(DeleteSubscript) \
- F(DeleteName) \
- F(TypeofName) \
- F(TypeofValue) \
- F(DeclareVar) \
- F(DefineArray) \
- F(DefineObjectLiteral) \
- F(CreateMappedArgumentsObject) \
- F(CreateUnmappedArgumentsObject) \
F(ConvertThisToObject) \
- F(Construct) \
+ F(ToObject) \
F(Jump) \
F(JumpTrue) \
F(JumpFalse) \
+ F(JumpNoException) \
+ F(JumpNotUndefined) \
F(CmpEqNull) \
F(CmpNeNull) \
F(CmpEqInt) \
@@ -263,8 +254,6 @@ QT_BEGIN_NAMESPACE
F(CmpStrictNotEqual) \
F(CmpIn) \
F(CmpInstanceOf) \
- F(JumpStrictEqualStackSlotInt) \
- F(JumpStrictNotEqualStackSlotInt) \
F(UNot) \
F(UPlus) \
F(UMinus) \
@@ -284,13 +273,60 @@ QT_BEGIN_NAMESPACE
F(UShrConst) \
F(ShrConst) \
F(ShlConst) \
+ F(Exp) \
F(Mul) \
F(Div) \
F(Mod) \
F(Sub) \
+ F(CallValue) \
+ F(CallProperty) \
+ F(CallPropertyLookup) \
+ F(CallElement) \
+ F(CallName) \
+ F(CallPossiblyDirectEval) \
+ F(CallGlobalLookup) \
+ F(CallScopeObjectProperty) \
+ F(CallContextObjectProperty) \
+ F(CallWithSpread) \
+ F(Construct) \
+ F(ConstructWithSpread) \
+ F(SetUnwindHandler) \
+ F(UnwindDispatch) \
+ F(UnwindToLabel) \
+ F(ThrowException) \
+ F(GetException) \
+ F(SetException) \
+ F(CreateCallContext) \
+ F(PushCatchContext) \
+ F(PushWithContext) \
+ F(PushBlockContext) \
+ F(CloneBlockContext) \
+ F(PopContext) \
+ F(GetIterator) \
+ F(IteratorNext) \
+ F(IteratorClose) \
+ F(DestructureRestElement) \
+ F(DeleteProperty) \
+ F(DeleteName) \
+ F(TypeofName) \
+ F(TypeofValue) \
+ F(DeclareVar) \
+ F(DefineArray) \
+ F(DefineObjectLiteral) \
+ F(CreateMappedArgumentsObject) \
+ F(CreateUnmappedArgumentsObject) \
+ F(CreateRestParameter) \
F(LoadQmlContext) \
- F(LoadQmlImportedScripts)
-#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::LoadQmlImportedScripts) + 1)
+ F(LoadQmlImportedScripts) \
+ F(Yield) \
+ F(Resume) \
+ F(CreateClass) \
+ F(LoadSuperConstructor) \
+ F(PushScriptContext) \
+ F(PopScriptContext) \
+ F(Debug) \
+
+#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1)
#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL)
// icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the
@@ -301,7 +337,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
-#define MOTH_INSTR_ENUM(I) I,
+#define MOTH_INSTR_ENUM(I) I, I##_Wide,
#define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I))
#define MOTH_EXPAND_FOR_MSVC(x) x
@@ -344,7 +380,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_COLLECT_NARGS(instr) \
INSTR_##instr(MOTH_COLLECT_ARG_COUNT)
#define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \
- nargs,
+ nargs, nargs,
#define MOTH_DECODE_ARG(arg, type, nargs, offset) \
arg = qFromLittleEndian<type>(qFromUnaligned<type>(reinterpret_cast<const type *>(code) - nargs + offset));
@@ -397,38 +433,53 @@ QT_BEGIN_NAMESPACE
#ifdef MOTH_COMPUTED_GOTO
/* collect jump labels */
#define COLLECT_LABELS(instr) \
- INSTR_##instr(GET_LABEL)
+ INSTR_##instr(GET_LABEL) \
+ INSTR_##instr(GET_LABEL_WIDE)
#define GET_LABEL_INSTRUCTION(name, ...) \
&&op_byte_##name,
-#define COLLECT_LABELS_WIDE(instr) \
- INSTR_##instr(GET_LABEL_WIDE)
#define GET_LABEL_WIDE_INSTRUCTION(name, ...) \
&&op_int_##name,
#define MOTH_JUMP_TABLE \
static const void *jumpTable[] = { \
- FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \
- FOR_EACH_MOTH_INSTR(COLLECT_LABELS_WIDE) \
+ FOR_EACH_MOTH_INSTR_ALL(COLLECT_LABELS) \
};
-#define MOTH_DISPATCH() \
+#define MOTH_DISPATCH_SINGLE() \
goto *jumpTable[*reinterpret_cast<const uchar *>(code)];
+
+#define MOTH_DISPATCH() \
+ MOTH_DISPATCH_SINGLE() \
+ op_byte_Nop: \
+ ++code; \
+ MOTH_DISPATCH_SINGLE() \
+ op_int_Nop: /* wide prefix */ \
+ ++code; \
+ goto *jumpTable[0x100 | *reinterpret_cast<const uchar *>(code)];
#else
#define MOTH_JUMP_TABLE
#define MOTH_INSTR_CASE_AND_JUMP(instr) \
- INSTR_##instr(GET_CASE_AND_JUMP)
-#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
- case static_cast<uchar>(Instr::Type::name): goto op_byte_##name;
-#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \
+ INSTR_##instr(GET_CASE_AND_JUMP) \
INSTR_##instr(GET_CASE_AND_JUMP_WIDE)
+#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
+ case Instr::Type::name: goto op_byte_##name;
#define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \
- case (static_cast<uchar>(Instr::Type::name) + MOTH_NUM_INSTRUCTIONS()): goto op_int_##name;
+ case Instr::Type::name##_Wide: goto op_int_##name;
#define MOTH_DISPATCH() \
- switch (static_cast<uchar>(*code)) { \
+ Instr::Type type = Instr::Type(static_cast<uchar>(*code)); \
+ dispatch: \
+ switch (type) { \
+ case Instr::Type::Nop: \
+ ++code; \
+ type = Instr::Type(static_cast<uchar>(*code)); \
+ goto dispatch; \
+ case Instr::Type::Nop_Wide: /* wide prefix */ \
+ ++code; \
+ type = Instr::Type(0x100 | static_cast<uchar>(*code)); \
+ goto dispatch; \
FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \
}
#endif
@@ -471,12 +522,29 @@ inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals,
union Instr
{
enum class Type {
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_ENUM)
};
- FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS)
+ static Type wideInstructionType(Type t) { return Type(int(t) | 1); }
+ static Type narrowInstructionType(Type t) { return Type(int(t) & ~1); }
+ static bool isWide(Type t) { return int(t) & 1; }
+ static bool isNarrow(Type t) { return !(int(t) & 1); }
+ static int encodedLength(Type t) { return int(t) >= 256 ? 2 : 1; }
+
+ static Type unpack(const uchar *c) { if (c[0] == 0x1) return Type(0x100 + c[1]); return Type(c[0]); }
+ static uchar *pack(uchar *c, Type t) {
+ if (uint(t) >= 256) {
+ c[0] = 0x1;
+ c[1] = uint(t) &0xff;
+ return c + 2;
+ }
+ c[0] = uchar(uint(t));
+ return c + 1;
+ }
+
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_STRUCTS)
- FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_INSTR_MEMBERS)
int argumentsAsInts[4];
};
@@ -487,8 +555,6 @@ struct InstrInfo
static int size(Instr::Type type);
};
-Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() < 128);
-
template<int N>
struct InstrMeta {
};
@@ -506,7 +572,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
reinterpret_cast<const char *>(&v), \
Size); } \
};
-FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_META_TEMPLATE);
#undef MOTH_INSTR_META_TEMPLATE
QT_WARNING_POP
@@ -517,7 +583,7 @@ class InstrData : public InstrMeta<InstrType>::DataType
struct Instruction {
#define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I;
-FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF)
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_DATA_TYPEDEF)
#undef MOTH_INSTR_DATA_TYPEDEF
private:
Instruction();
diff --git a/src/qml/configure.json b/src/qml/configure.json
index 681cecea99..481cc553ae 100644
--- a/src/qml/configure.json
+++ b/src/qml/configure.json
@@ -40,6 +40,59 @@
],
"output": [ "privateFeature" ]
},
+ "qml-preview": {
+ "label": "Command line QML Preview tool",
+ "purpose": "Updates QML documents in your application live as you change them on disk",
+ "section": "QML",
+ "condition": [
+ "features.commandlineparser",
+ "features.localserver",
+ "features.process",
+ "features.qml-debug"
+ ],
+ "output": [ "privateFeature" ]
+ },
+ "qml-devtools": {
+ "label": "QML Development Tools",
+ "purpose": "Provides the QmlDevtools library and various utilities.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-sequence-object": {
+ "label": "QML sequence object",
+ "purpose": "Supports mapping sequence types into QML.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-list-model": {
+ "label": "QML list model",
+ "purpose": "Provides the ListModel QML type.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-xml-http-request": {
+ "label": "QML XML http request",
+ "purpose": "Provides support for sending XML http requests.",
+ "section": "QML",
+ "condition": [
+ "features.xmlstreamreader",
+ "features.qml-network"
+ ],
+ "output": [ "privateFeature" ]
+ },
+ "qml-locale": {
+ "label": "QML Locale",
+ "purpose": "Provides support for locales in QML.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-animation": {
+ "label": "QML Animations",
+ "purpose": "Provides support for animations and timers in QML.",
+ "section": "QML",
+ "condition": "features.animation",
+ "output": [ "privateFeature" ]
+ },
"qml-delegate-model": {
"label": "QML delegate model",
"purpose": "Provides the DelegateModel QML type.",
@@ -54,6 +107,10 @@
"entries": [
"qml-network",
"qml-debug",
+ "qml-sequence-object",
+ "qml-list-model",
+ "qml-xml-http-request",
+ "qml-locale",
"qml-delegate-model"
]
}
diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h
index c63e694c7e..f39f8fccd2 100644
--- a/src/qml/debugger/qqmlabstractprofileradapter_p.h
+++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h
@@ -73,13 +73,13 @@ public:
~QQmlAbstractProfilerAdapter() override {}
void setService(QQmlProfilerService *new_service) { service = new_service; }
- virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) = 0;
+ virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0;
void startProfiling(quint64 features);
void stopProfiling();
- void reportData(bool trackLocations) { emit dataRequested(trackLocations); }
+ void reportData() { emit dataRequested(); }
void stopWaiting() { waiting = false; }
void startWaiting() { waiting = true; }
@@ -96,7 +96,7 @@ signals:
void profilingDisabled();
void profilingDisabledWhileWaiting();
- void dataRequested(bool trackLocations);
+ void dataRequested();
void referenceTimeKnown(const QElapsedTimer &timer);
protected:
diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp
index d9f51ce09f..0ef40d6911 100644
--- a/src/qml/debugger/qqmldebugconnector.cpp
+++ b/src/qml/debugger/qqmldebugconnector.cpp
@@ -82,7 +82,7 @@ Q_GLOBAL_STATIC(QQmlDebugConnectorParams, qmlDebugConnectorParams)
void QQmlDebugConnector::setPluginKey(const QString &key)
{
QQmlDebugConnectorParams *params = qmlDebugConnectorParams();
- if (params) {
+ if (params && params->pluginKey != key) {
if (params->instance)
qWarning() << "QML debugger: Cannot set plugin key after loading the plugin.";
else
diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp
index 8c0bd73822..da0b14dd85 100644
--- a/src/qml/debugger/qqmlprofiler.cpp
+++ b/src/qml/debugger/qqmlprofiler.cpp
@@ -59,19 +59,18 @@ void QQmlProfiler::startProfiling(quint64 features)
void QQmlProfiler::stopProfiling()
{
featuresEnabled = false;
- reportData(true);
+ reportData();
m_locations.clear();
}
-void QQmlProfiler::reportData(bool trackLocations)
+void QQmlProfiler::reportData()
{
LocationHash resolved;
resolved.reserve(m_locations.size());
for (auto it = m_locations.begin(), end = m_locations.end(); it != end; ++it) {
- if (!trackLocations || !it->sent) {
+ if (!it->sent) {
resolved.insert(it.key(), it.value());
- if (trackLocations)
- it->sent = true;
+ it->sent = true;
}
}
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index 287c53ea05..d01e2bc429 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -383,7 +383,7 @@ public:
void startProfiling(quint64 features);
void stopProfiling();
- void reportData(bool trackLocations);
+ void reportData();
void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
signals:
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc
index 8a606d672a..7fb4724f73 100644
--- a/src/qml/doc/src/cppintegration/data.qdoc
+++ b/src/qml/doc/src/cppintegration/data.qdoc
@@ -262,8 +262,8 @@ method.
\section2 Sequence Type to JavaScript Array
-Certain C++ sequence types are supported transparently in QML as JavaScript
-\c Array types.
+Certain C++ sequence types are supported transparently in QML to behave like
+JavaScript \c Array types.
In particular, QML currently supports:
\list
diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
index aefdfd9401..52534b4a62 100644
--- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
+++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
@@ -255,9 +255,6 @@ type, and so cannot provide the necessary QML property characteristics
through the Qt meta object system, such as signal notifications when a list
is modified.
-QQmlListProperty is a template class that can be conveniently constructed from
-a QList value.
-
For example, the \c MessageBoard class below has a \c messages property of
type QQmlListProperty that stores a list of \c Message instances:
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 55ca040af6..62c0f5d81b 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -538,30 +538,6 @@
}
\endqml
- Since singleton types do not have an associated QQmlContext object, then within the functions of a QObject-derived
- type that is registered as a singleton type implementation the QML context and engine information is not available.
- The QQmlEngine::contextForObject() function returns NULL when supplied with a pointer to an QObject that
- implements a singleton type.
-
- Extending the above example:
-
- \code
- class SingletonTypeExample : public QObject
- {
- ...
-
- Q_INVOKABLE void doSomethingElse()
- {
- // QML Engine/Context information is not accessible here:
- Q_ASSERT(QQmlEngine::contextForObject(this) == 0);
- Q_ASSERT(qmlContext(this) == 0);
- Q_ASSERT(qmlEngine(this) == 0);
- }
-
- ...
- }
- \endcode
-
\sa {Choosing the Correct Integration Method Between C++ and QML}
*/
@@ -662,3 +638,25 @@
are registered for that version. This is particularly useful for keeping the
versions of related modules in sync.
*/
+
+/*!
+ \since 5.12
+ \fn int qmlTypeId(const char* uri, int versionMajor, int versionMinor, const char *qmlName);
+ \relates QQmlEngine
+
+ Returns the QML type id of a type that was registered with the
+ name \a qmlName in a particular \a uri and a version specified in \a
+ versionMajor and \a versionMinor.
+
+ This function returns the same value as the QML type registration functions
+ such as qmlRegisterType() and qmlRegisterSingletonType().
+
+ If \a qmlName, \a uri and \a versionMajor match a registered type, but the
+ specified minor version in \a versionMinor is higher, then the id of the type
+ with the closest minor version is returned.
+
+ Returns -1 if no matching type was found or one of the given parameters
+ was invalid.
+
+ \sa qmlRegisterType(), qmlRegisterSingletonType()
+*/
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri
index 2080844d93..9e3cf44387 100644
--- a/src/qml/jit/jit.pri
+++ b/src/qml/jit/jit.pri
@@ -2,9 +2,11 @@ INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
SOURCES += \
- $$PWD/qv4jit.cpp \
+ $$PWD/qv4jithelpers.cpp \
+ $$PWD/qv4baselinejit.cpp \
$$PWD/qv4assembler.cpp
HEADERS += \
- $$PWD/qv4jit_p.h \
+ $$PWD/qv4jithelpers_p.h \
+ $$PWD/qv4baselinejit_p.h \
$$PWD/qv4assembler_p.h
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index 9786293e4c..7d668950d7 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -44,6 +44,7 @@
#include "qv4assembler_p.h"
#include <private/qv4function_p.h>
#include <private/qv4runtime_p.h>
+#include <private/qv4stackframe_p.h>
#include <wtf/Vector.h>
#include <assembler/MacroAssembler.h>
@@ -680,6 +681,12 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
store64(AccumulatorRegister, addr);
}
+ void moveReg(Address sourceRegAddress, Address destRegAddress)
+ {
+ load64(sourceRegAddress, ScratchRegister);
+ store64(ScratchRegister, destRegAddress);
+ }
+
void loadString(int stringId)
{
loadAccumulator(loadStringAddress(stringId));
@@ -700,6 +707,17 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
}
+ void jumpNotUndefined(int offset)
+ {
+ auto jump = branch64(NotEqual, AccumulatorRegister, TrustedImm64(0));
+ patches.push_back({ jump, offset });
+ }
+
+ Jump jumpEmpty()
+ {
+ return branch64(Equal, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue()));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
@@ -803,26 +821,6 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
return branch32(Equal, TrustedImm32(3), ScratchRegister);
}
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load64(lhsAddr, ScratchRegister);
- Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
- Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ equal, offset });
- isUndef.link(this);
- }
-
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load64(lhsAddr, ScratchRegister);
- Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
- patches.push_back({ isUndef, offset });
- Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
- }
-
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg == NoRegister)
@@ -960,6 +958,16 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
store32(AccumulatorRegisterTag, addr);
}
+ void moveReg(Address sourceRegAddress, Address destRegAddress)
+ {
+ load32(sourceRegAddress, ReturnValueRegisterValue);
+ sourceRegAddress.offset += 4;
+ load32(sourceRegAddress, ReturnValueRegisterTag);
+ store32(ReturnValueRegisterValue, destRegAddress);
+ destRegAddress.offset += 4;
+ store32(ReturnValueRegisterTag, destRegAddress);
+ }
+
void loadString(int stringId)
{
load32(loadStringAddress(stringId), AccumulatorRegisterValue);
@@ -1135,6 +1143,19 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
push(TrustedImm32(v));
}
+ void jumpNotUndefined(int offset)
+ {
+ move(AccumulatorRegisterTag, ScratchRegister);
+ or32(AccumulatorRegisterValue, ScratchRegister);
+ auto jump = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
+ patches.push_back({ jump, offset });
+ }
+
+ Jump jumpEmpty()
+ {
+ return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
@@ -1174,34 +1195,6 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
done.link(this);
}
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load32(lhsAddr, ScratchRegister);
- Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs));
- Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefVal, offset });
- lhsAddr.offset += 4;
- load32(lhsAddr, ScratchRegister);
- Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefTag, offset });
- notEqInt.link(this);
- }
-
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load32(lhsAddr, ScratchRegister);
- Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
- Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
- lhsAddr.offset += 4;
- load32(lhsAddr, ScratchRegister);
- Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
- patches.push_back({ equalUndef, offset });
- notUndefValue.link(this);
- }
-
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg != NoRegister)
@@ -1415,7 +1408,6 @@ void Assembler::link(Function *function)
function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
-#if defined(Q_OS_LINUX)
// This implements writing of JIT'd addresses so that perf can find the
// symbol names.
//
@@ -1423,7 +1415,7 @@ void Assembler::link(Function *function)
// content, for more information, see:
// https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP");
- if (doProfile) {
+ if (Q_UNLIKELY(doProfile)) {
static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map")
.arg(QCoreApplication::applicationPid()));
static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly);
@@ -1441,7 +1433,6 @@ void Assembler::link(Function *function)
perfMapFile.flush();
}
}
-#endif
}
void Assembler::addLabel(int offset)
@@ -1469,6 +1460,11 @@ void Assembler::loadReg(int reg)
pasm()->loadAccumulator(regAddr(reg));
}
+void JIT::Assembler::moveReg(int sourceReg, int destReg)
+{
+ pasm()->moveReg(regAddr(sourceReg), regAddr(destReg));
+}
+
void Assembler::storeReg(int reg)
{
pasm()->storeAccumulator(regAddr(reg));
@@ -1981,14 +1977,19 @@ void Assembler::jumpFalse(int offset)
});
}
-void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
+void Assembler::jumpNoException(int offset)
{
- pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset);
+ auto jump = pasm()->branch32(
+ PlatformAssembler::Equal,
+ PlatformAssembler::Address(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, hasException)),
+ TrustedImm32(0));
+ pasm()->patches.push_back({ jump, offset });
}
-void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
+void Assembler::jumpNotUndefined(int offset)
{
- pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset);
+ pasm()->jumpNotUndefined(offset);
}
void Assembler::prepareCallWithArgCount(int argc)
@@ -2182,39 +2183,70 @@ void Assembler::getException()
void Assembler::setException()
{
+ auto noException = pasm()->jumpEmpty();
Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister));
addr.offset = offsetof(EngineBase, hasException);
Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
pasm()->store8(TrustedImm32(1), addr);
+ noException.link(pasm());
}
-void Assembler::setExceptionHandler(int offset)
+void Assembler::setUnwindHandler(int offset)
{
auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
pasm()->ehTargets.push_back({ l, offset });
}
-void Assembler::clearExceptionHandler()
+void Assembler::clearUnwindHandler()
{
pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
}
-void Assembler::pushCatchContext(int name, int reg)
+void JIT::Assembler::unwindDispatch()
{
- pasm()->copyReg(pasm()->contextAddress(), regAddr(reg));
- prepareCallWithArgCount(2);
- passInt32AsArg(name, 1);
+ checkException();
+ pasm()->load32(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)), PlatformAssembler::ScratchRegister);
+ auto noUnwind = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ pasm()->sub32(TrustedImm32(1), PlatformAssembler::ScratchRegister);
+ pasm()->store32(PlatformAssembler::ScratchRegister, Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ auto jump = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ gotoCatchException();
+ jump.link(pasm());
+
+ pasm()->loadPtr(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)), PlatformAssembler::ScratchRegister);
+ pasm()->jump(PlatformAssembler::ScratchRegister);
+
+ noUnwind.link(pasm());
+}
+
+void JIT::Assembler::unwindToLabel(int level, int offset)
+{
+ auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)));
+ pasm()->ehTargets.push_back({ l, offset });
+ pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ gotoCatchException();
+}
+
+void Assembler::pushCatchContext(int index, int name)
+{
+ prepareCallWithArgCount(3);
+ passInt32AsArg(name, 2);
+ passInt32AsArg(index, 1);
passRegAsArg(CallData::Context, 0);
IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, ResultInAccumulator);
pasm()->storeAccumulator(pasm()->contextAddress());
}
-void Assembler::popContext(int reg)
+void Assembler::popContext()
{
- pasm()->copyReg(regAddr(reg), pasm()->contextAddress());
+ Heap::CallContext ctx;
+ Q_UNUSED(ctx)
+ pasm()->loadPointerFromValue(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
+ pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset));
+ pasm()->storeAccumulator(regAddr(CallData::Context));
}
void Assembler::ret()
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index 37d4232a17..2cf59f53ee 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -90,6 +90,7 @@ public:
void loadConst(int constIndex);
void copyConst(int constIndex, int destReg);
void loadReg(int reg);
+ void moveReg(int sourceReg, int destReg);
void storeReg(int reg);
void loadLocal(int index, int level = 0);
void storeLocal(int index, int level = 0);
@@ -140,8 +141,8 @@ public:
void jump(int offset);
void jumpTrue(int offset);
void jumpFalse(int offset);
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset);
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset);
+ void jumpNoException(int offset);
+ void jumpNotUndefined(int offset);
// stuff for runtime calls
void prepareCallWithArgCount(int argc);
@@ -160,10 +161,12 @@ public:
void gotoCatchException();
void getException();
void setException();
- void setExceptionHandler(int offset);
- void clearExceptionHandler();
- void pushCatchContext(int name, int reg);
- void popContext(int reg);
+ void setUnwindHandler(int offset);
+ void clearUnwindHandler();
+ void unwindDispatch();
+ void unwindToLabel(int level, int offset);
+ void pushCatchContext(int index, int name);
+ void popContext();
// other stuff
void ret();
diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4baselinejit.cpp
index bc46c0ca1d..9132b194a5 100644
--- a/src/qml/jit/qv4jit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -37,9 +37,11 @@
**
****************************************************************************/
-#include "qv4jit_p.h"
+#include "qv4baselinejit_p.h"
+#include "qv4jithelpers_p.h"
#include "qv4assembler_p.h"
#include <private/qv4lookup_p.h>
+#include <private/qv4generatorobject_p.h>
#ifdef V4_ENABLE_JIT
@@ -48,42 +50,6 @@ using namespace QV4;
using namespace QV4::JIT;
using namespace QV4::Moth;
-ByteCodeHandler::~ByteCodeHandler()
-{
-}
-
-#define DISPATCH_INSTRUCTION(name, nargs, ...) \
- generate_##name( \
- __VA_ARGS__ \
- );
-
-#define DECODE_AND_DISPATCH(instr) \
- { \
- INSTR_##instr(MOTH_DECODE_WITH_BASE) \
- Q_UNUSED(base_ptr); \
- startInstruction(Instr::Type::instr); \
- _offset = code - start; \
- INSTR_##instr(DISPATCH) \
- endInstruction(Instr::Type::instr); \
- continue; \
- }
-
-void ByteCodeHandler::decode(const char *code, uint len)
-{
- MOTH_JUMP_TABLE;
-
- const char *start = code;
- const char *end = code + len;
- while (code < end) {
- MOTH_DISPATCH()
-
- FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH)
- }
-}
-
-#undef DECODE_AND_DISPATCH
-#undef DISPATCH_INSTRUCTION
-
BaselineJIT::BaselineJIT(Function *function)
: function(function)
, as(new Assembler(function->compilationUnit->constants))
@@ -95,10 +61,12 @@ BaselineJIT::~BaselineJIT()
void BaselineJIT::generate()
{
// qDebug()<<"jitting" << function->name()->toQString();
- collectLabelsInBytecode();
+ const char *code = function->codeData;
+ uint len = function->compiledFunction->codeSize;
+ labels = collectLabelsInBytecode(code, len);
as->generatePrologue();
- decode(reinterpret_cast<const char *>(function->codeData), function->compiledFunction->codeSize);
+ decode(code, len);
as->generateEpilogue();
as->link(function);
@@ -168,8 +136,8 @@ void BaselineJIT::generate_StoreReg(int reg)
void BaselineJIT::generate_MoveReg(int srcReg, int destReg)
{
- as->loadReg(srcReg);
- as->storeReg(destReg);
+ // Don't clobber the accumulator.
+ as->moveReg(srcReg, destReg);
}
void BaselineJIT::generate_LoadLocal(int index)
@@ -226,19 +194,13 @@ void BaselineJIT::generate_LoadName(int name)
as->checkException();
}
-static ReturnedValue loadGlobalLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index)
-{
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->globalGetter(l, engine);
-}
-
void BaselineJIT::generate_LoadGlobalLookup(int index)
{
as->prepareCallWithArgCount(3);
as->passInt32AsArg(index, 2);
as->passFunctionAsArg(1);
as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(loadGlobalLookupHelper, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::loadGlobalLookup, Assembler::ResultInAccumulator);
as->checkException();
}
@@ -266,18 +228,7 @@ void BaselineJIT::generate_StoreNameStrict(int name)
as->checkException();
}
-void BaselineJIT::generate_LoadElement(int base, int index)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passRegAsArg(index, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadElementA(int base)
+void BaselineJIT::generate_LoadElement(int base)
{
STORE_IP();
STORE_ACC();
@@ -289,13 +240,6 @@ void BaselineJIT::generate_LoadElementA(int base)
as->checkException();
}
-static void storeElementHelper(QV4::Function *f, const Value &base, const Value &index, const Value &value)
-{
- auto engine = f->internalClass->engine;
- if (!Runtime::method_storeElement(engine, base, index, value) && f->isStrict())
- engine->throwTypeError();
-}
-
void BaselineJIT::generate_StoreElement(int base, int index)
{
STORE_IP();
@@ -304,22 +248,12 @@ void BaselineJIT::generate_StoreElement(int base, int index)
as->passAccumulatorAsArg(3);
as->passRegAsArg(index, 2);
as->passRegAsArg(base, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(storeElementHelper, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadProperty(int name, int base)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(name, 2);
- as->passRegAsArg(base, 1);
as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeElement, Assembler::IgnoreResult);
as->checkException();
}
-void BaselineJIT::generate_LoadPropertyA(int name)
+
+void BaselineJIT::generate_LoadProperty(int name)
{
STORE_IP();
STORE_ACC();
@@ -331,78 +265,69 @@ void BaselineJIT::generate_LoadPropertyA(int name)
as->checkException();
}
-static ReturnedValue getLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index, const QV4::Value &base)
-{
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->getter(l, engine, base);
-}
-
-void BaselineJIT::generate_GetLookup(int index, int base)
+void BaselineJIT::generate_GetLookup(int index)
{
STORE_IP();
+ STORE_ACC();
as->prepareCallWithArgCount(4);
- as->passRegAsArg(base, 3);
+ as->passAccumulatorAsArg(3);
as->passInt32AsArg(index, 2);
as->passFunctionAsArg(1);
as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::getLookup, Assembler::ResultInAccumulator);
as->checkException();
}
-void BaselineJIT::generate_GetLookupA(int index)
+void BaselineJIT::generate_StoreProperty(int name, int base)
{
STORE_IP();
STORE_ACC();
as->prepareCallWithArgCount(4);
as->passAccumulatorAsArg(3);
- as->passInt32AsArg(index, 2);
- as->passFunctionAsArg(1);
+ as->passInt32AsArg(name, 2);
+ as->passRegAsArg(base, 1);
as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeProperty, Assembler::IgnoreResult);
as->checkException();
}
-static void storePropertyHelper(QV4::Function *f, const Value &base, int name, const Value &value)
-{
- auto engine = f->internalClass->engine;
- if (!Runtime::method_storeProperty(engine, base, name, value) && f->isStrict())
- engine->throwTypeError();
-}
-
-void BaselineJIT::generate_StoreProperty(int name, int base)
+void BaselineJIT::generate_SetLookup(int index, int base)
{
STORE_IP();
STORE_ACC();
as->prepareCallWithArgCount(4);
as->passAccumulatorAsArg(3);
- as->passInt32AsArg(name, 2);
- as->passRegAsArg(base, 1);
+ as->passRegAsArg(base, 2);
+ as->passInt32AsArg(index, 1);
as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(storePropertyHelper, Assembler::IgnoreResult);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::setLookup, Assembler::ResultInAccumulator);
as->checkException();
}
-static void setLookupHelper(QV4::Function *f, int index, QV4::Value &base, const QV4::Value &value)
+void BaselineJIT::generate_LoadSuperProperty(int property)
{
- ExecutionEngine *engine = f->internalClass->engine;
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- if (!l->setter(l, engine, base, value) && f->isStrict())
- engine->throwTypeError();
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passRegAsArg(property, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperProperty, Assembler::ResultInAccumulator);
+ as->checkException();
}
-void BaselineJIT::generate_SetLookup(int index, int base)
+void BaselineJIT::generate_StoreSuperProperty(int property)
{
STORE_IP();
STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passRegAsArg(base, 2);
- as->passInt32AsArg(index, 1);
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passRegAsArg(property, 1);
as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(setLookupHelper, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeSuperProperty, Assembler::IgnoreResult);
as->checkException();
}
+
void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex)
{
STORE_ACC();
@@ -462,6 +387,18 @@ void BaselineJIT::generate_LoadIdObject(int index, int base)
as->checkException();
}
+void BaselineJIT::generate_Yield()
+{
+ // #####
+ Q_UNREACHABLE();
+}
+
+void BaselineJIT::generate_Resume(int)
+{
+ // #####
+ Q_UNREACHABLE();
+}
+
void BaselineJIT::generate_CallValue(int name, int argc, int argv)
{
STORE_IP();
@@ -574,14 +511,68 @@ void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int
as->checkException();
}
-void BaselineJIT::generate_SetExceptionHandler(int offset)
+
+void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passRegAsArg(argv, 3);
+ as->passRegAsArg(thisObject, 2);
+ as->passRegAsArg(func, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithSpread, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+
+void BaselineJIT::generate_Construct(int func, int argc, int argv)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passRegAsArg(argv, 3);
+ as->passAccumulatorAsArg(2);
+ as->passRegAsArg(func, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passRegAsArg(argv, 3);
+ as->passAccumulatorAsArg(2);
+ as->passRegAsArg(func, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_constructWithSpread, Assembler::ResultInAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_SetUnwindHandler(int offset)
{
if (offset)
- as->setExceptionHandler(instructionOffset() + offset);
+ as->setUnwindHandler(instructionOffset() + offset);
else
- as->clearExceptionHandler();
+ as->clearUnwindHandler();
}
+void BaselineJIT::generate_UnwindDispatch()
+{
+ as->unwindDispatch();
+}
+
+void BaselineJIT::generate_UnwindToLabel(int level, int offset)
+{
+ as->unwindToLabel(level, instructionOffset() + offset);
+}
+
+
void BaselineJIT::generate_ThrowException()
{
STORE_IP();
@@ -604,118 +595,119 @@ void BaselineJIT::generate_CreateCallContext()
as->storeHeapObject(CallData::Context);
}
-void BaselineJIT::generate_PushCatchContext(int name, int reg) { as->pushCatchContext(name, reg); }
+void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); }
-static void pushWithContextHelper(ExecutionEngine *engine, QV4::Value *stack, int reg)
-{
- QV4::Value &accumulator = stack[CallData::Accumulator];
- accumulator = accumulator.toObject(engine);
- if (engine->hasException)
- return;
- stack[reg] = stack[CallData::Context];
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- stack[CallData::Context] = Runtime::method_createWithContext(c, accumulator);
-}
-
-void BaselineJIT::generate_PushWithContext(int reg)
+void BaselineJIT::generate_PushWithContext()
{
STORE_IP();
as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(reg, 2);
+ as->prepareCallWithArgCount(2);
as->passRegAsArg(0, 1);
as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(pushWithContextHelper, Assembler::IgnoreResult);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_createWithContext, Assembler::IgnoreResult); // keeps result in return value register
as->checkException();
+ as->storeHeapObject(CallData::Context);
}
-void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); }
-
-void BaselineJIT::generate_ForeachIteratorObject()
+void BaselineJIT::generate_PushBlockContext(int index)
{
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(2);
- as->passAccumulatorAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachIterator, Assembler::ResultInAccumulator);
- as->checkException();
+ as->passInt32AsArg(index, 1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::pushBlockContext, Assembler::IgnoreResult);
}
-void BaselineJIT::generate_ForeachNextPropertyName()
+void BaselineJIT::generate_CloneBlockContext()
{
as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(1);
- as->passAccumulatorAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachNextPropertyName,
- Assembler::ResultInAccumulator);
+ as->passRegAsArg(CallData::Context, 0);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::cloneBlockContext, Assembler::IgnoreResult);
+}
+
+void BaselineJIT::generate_PushScriptContext(int index)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passEngineAsArg(1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::pushScriptContext, Assembler::IgnoreResult);
+}
+
+void BaselineJIT::generate_PopScriptContext()
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passEngineAsArg(1);
+ as->passRegAsArg(0, 0);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::popScriptContext, Assembler::IgnoreResult);
+}
+
+void BaselineJIT::generate_PopContext() { as->popContext(); }
+
+void BaselineJIT::generate_GetIterator(int iterator)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(iterator, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_getIterator, Assembler::ResultInAccumulator);
as->checkException();
}
-static ReturnedValue deleteMemberHelper(QV4::Function *function, const QV4::Value &base, int member)
+void BaselineJIT::generate_IteratorNext(int value)
{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteMember(engine, base, member)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passRegAsArg(value, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNext, Assembler::ResultInAccumulator);
+ as->checkException();
}
-void BaselineJIT::generate_DeleteMember(int member, int base)
+void BaselineJIT::generate_IteratorClose(int done)
{
- STORE_IP();
+ as->saveAccumulatorInFrame();
as->prepareCallWithArgCount(3);
- as->passInt32AsArg(member, 2);
- as->passRegAsArg(base, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(deleteMemberHelper, Assembler::ResultInAccumulator);
+ as->passRegAsArg(done, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorClose, Assembler::ResultInAccumulator);
as->checkException();
}
-static ReturnedValue deleteSubscriptHelper(QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
+void BaselineJIT::generate_DestructureRestElement()
{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteElement(engine, base, index)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_destructureRestElement, Assembler::ResultInAccumulator);
+ as->checkException();
}
-void BaselineJIT::generate_DeleteSubscript(int base, int index)
+void BaselineJIT::generate_DeleteProperty(int base, int index)
{
STORE_IP();
as->prepareCallWithArgCount(3);
as->passRegAsArg(index, 2);
as->passRegAsArg(base, 1);
as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(deleteSubscriptHelper, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::deleteProperty, Assembler::ResultInAccumulator);
as->checkException();
}
-static ReturnedValue deleteNameHelper(QV4::Function *function, int name)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteName(engine, name)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
void BaselineJIT::generate_DeleteName(int name)
{
STORE_IP();
as->prepareCallWithArgCount(2);
as->passInt32AsArg(name, 1);
as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(deleteNameHelper, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::deleteName, Assembler::ResultInAccumulator);
as->checkException();
}
@@ -754,17 +746,26 @@ void BaselineJIT::generate_DefineArray(int argc, int args)
JIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, Assembler::ResultInAccumulator);
}
-void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int arrayValueCount,
- int arrayGetterSetterCountAndFlags, int args)
+void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
{
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(arrayGetterSetterCountAndFlags, 4);
- as->passInt32AsArg(arrayValueCount, 3);
- as->passInt32AsArg(internalClassId, 2);
- as->passRegAsArg(args, 1);
+ as->prepareCallWithArgCount(4);
+ as->passRegAsArg(args, 3);
+ as->passInt32AsArg(argc, 2);
+ as->passInt32AsArg(internalClassId, 1);
as->passEngineAsArg(0);
JIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, Assembler::ResultInAccumulator);
}
+
+void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int computedNames)
+{
+ as->prepareCallWithArgCount(4);
+ as->passRegAsArg(computedNames, 3);
+ as->passRegAsArg(heritage, 2);
+ as->passInt32AsArg(classIndex, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_createClass, Assembler::ResultInAccumulator);
+}
+
void BaselineJIT::generate_CreateMappedArgumentsObject()
{
as->prepareCallWithArgCount(1);
@@ -781,15 +782,12 @@ void BaselineJIT::generate_CreateUnmappedArgumentsObject()
Assembler::ResultInAccumulator);
}
-static void convertThisToObjectHelper(ExecutionEngine *engine, Value *t)
+void BaselineJIT::generate_CreateRestParameter(int argIndex)
{
- if (!t->isObject()) {
- if (t->isNullOrUndefined()) {
- *t = engine->globalObject->asReturnedValue();
- } else {
- *t = t->toObject(engine)->asReturnedValue();
- }
- }
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(argIndex, 1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Runtime::method_createRestParameter, Assembler::ResultInAccumulator);
}
void BaselineJIT::generate_ConvertThisToObject()
@@ -797,25 +795,35 @@ void BaselineJIT::generate_ConvertThisToObject()
as->prepareCallWithArgCount(2);
as->passRegAsArg(CallData::This, 1);
as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(convertThisToObjectHelper, Assembler::IgnoreResult);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::convertThisToObject, Assembler::IgnoreResult);
as->checkException();
}
-void BaselineJIT::generate_Construct(int func, int argc, int argv)
+void BaselineJIT::generate_LoadSuperConstructor()
{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(argc, 3);
- as->passRegAsArg(argv, 2);
- as->passRegAsArg(func, 1);
+ as->prepareCallWithArgCount(2);
+ as->passRegAsArg(CallData::Function, 1);
as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::loadSuperConstructor, Assembler::ResultInAccumulator);
as->checkException();
}
+void BaselineJIT::generate_ToObject()
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::toObject, Assembler::ResultInAccumulator);
+ as->checkException();
+
+}
+
void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); }
void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); }
void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); }
+void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(instructionOffset() + offset); }
+void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(instructionOffset() + offset); }
void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); }
void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); }
@@ -852,16 +860,6 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs)
as->checkException();
}
-void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
-void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
void BaselineJIT::generate_UNot() { as->unot(); }
void BaselineJIT::generate_UPlus() { as->toNumber(); }
void BaselineJIT::generate_UMinus() { as->uminus(); }
@@ -884,6 +882,15 @@ void BaselineJIT::generate_UShrConst(int rhs) { as->ushrConst(rhs); }
void BaselineJIT::generate_ShrConst(int rhs) { as->shrConst(rhs); }
void BaselineJIT::generate_ShlConst(int rhs) { as->shlConst(rhs); }
+void BaselineJIT::generate_Exp(int lhs) {
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passRegAsArg(lhs, 0);
+ JIT_GENERATE_RUNTIME_CALL(Helpers::exp, Assembler::ResultInAccumulator);
+ as->checkException();
+}
void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); }
void BaselineJIT::generate_Div(int lhs) { as->div(lhs); }
void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); }
@@ -928,401 +935,4 @@ void BaselineJIT::endInstruction(Instr::Type instr)
Q_UNUSED(instr);
}
-#define MOTH_UNUSED_ARGS0()
-#define MOTH_UNUSED_ARGS1(arg) \
- Q_UNUSED(arg);
-#define MOTH_UNUSED_ARGS2(arg1, arg2) \
- Q_UNUSED(arg1); \
- Q_UNUSED(arg2);
-#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \
- Q_UNUSED(arg1); \
- Q_UNUSED(arg2); \
- Q_UNUSED(arg3);
-#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \
- Q_UNUSED(arg1); \
- Q_UNUSED(arg2); \
- Q_UNUSED(arg3); \
- Q_UNUSED(arg4);
-
-#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \
- MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__))
-
-#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \
- MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__)
-
-#define MOTH_BEGIN_INSTR(instr) \
- { \
- INSTR_##instr(MOTH_DECODE_WITH_BASE) \
- INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \
- Q_UNUSED(base_ptr);
-
-#define MOTH_END_INSTR(instr) \
- continue; \
- }
-
-void BaselineJIT::collectLabelsInBytecode()
-{
- MOTH_JUMP_TABLE;
-
- const auto addLabel = [&](int offset) {
- Q_ASSERT(offset >= 0 && offset < static_cast<int>(function->compiledFunction->codeSize));
- labels.push_back(offset);
- };
-
- const char *code = reinterpret_cast<const char *>(function->codeData);
- const char *start = code;
- const char *end = code + function->compiledFunction->codeSize;
- while (code < end) {
- MOTH_DISPATCH()
- Q_UNREACHABLE();
-
- MOTH_BEGIN_INSTR(LoadReg)
- MOTH_END_INSTR(LoadReg)
-
- MOTH_BEGIN_INSTR(StoreReg)
- MOTH_END_INSTR(StoreReg)
-
- MOTH_BEGIN_INSTR(MoveReg)
- MOTH_END_INSTR(MoveReg)
-
- MOTH_BEGIN_INSTR(LoadConst)
- MOTH_END_INSTR(LoadConst)
-
- MOTH_BEGIN_INSTR(LoadNull)
- MOTH_END_INSTR(LoadNull)
-
- MOTH_BEGIN_INSTR(LoadZero)
- MOTH_END_INSTR(LoadZero)
-
- MOTH_BEGIN_INSTR(LoadTrue)
- MOTH_END_INSTR(LoadTrue)
-
- MOTH_BEGIN_INSTR(LoadFalse)
- MOTH_END_INSTR(LoadFalse)
-
- MOTH_BEGIN_INSTR(LoadUndefined)
- MOTH_END_INSTR(LoadUndefined)
-
- MOTH_BEGIN_INSTR(LoadInt)
- MOTH_END_INSTR(LoadInt)
-
- MOTH_BEGIN_INSTR(MoveConst)
- MOTH_END_INSTR(MoveConst)
-
- MOTH_BEGIN_INSTR(LoadLocal)
- MOTH_END_INSTR(LoadLocal)
-
- MOTH_BEGIN_INSTR(StoreLocal)
- MOTH_END_INSTR(StoreLocal)
-
- MOTH_BEGIN_INSTR(LoadScopedLocal)
- MOTH_END_INSTR(LoadScopedLocal)
-
- MOTH_BEGIN_INSTR(StoreScopedLocal)
- MOTH_END_INSTR(StoreScopedLocal)
-
- MOTH_BEGIN_INSTR(LoadRuntimeString)
- MOTH_END_INSTR(LoadRuntimeString)
-
- MOTH_BEGIN_INSTR(MoveRegExp)
- MOTH_END_INSTR(MoveRegExp)
-
- MOTH_BEGIN_INSTR(LoadClosure)
- MOTH_END_INSTR(LoadClosure)
-
- MOTH_BEGIN_INSTR(LoadName)
- MOTH_END_INSTR(LoadName)
-
- MOTH_BEGIN_INSTR(LoadGlobalLookup)
- MOTH_END_INSTR(LoadGlobalLookup)
-
- MOTH_BEGIN_INSTR(StoreNameSloppy)
- MOTH_END_INSTR(StoreNameSloppy)
-
- MOTH_BEGIN_INSTR(StoreNameStrict)
- MOTH_END_INSTR(StoreNameStrict)
-
- MOTH_BEGIN_INSTR(LoadElement)
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(StoreElement)
- MOTH_END_INSTR(StoreElement)
-
- MOTH_BEGIN_INSTR(LoadProperty)
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
- MOTH_END_INSTR(LoadElementA)
-
- MOTH_BEGIN_INSTR(GetLookup)
- MOTH_END_INSTR(GetLookup)
-
- MOTH_BEGIN_INSTR(GetLookupA)
- MOTH_END_INSTR(GetLookupA)
-
- MOTH_BEGIN_INSTR(StoreProperty)
- MOTH_END_INSTR(StoreProperty)
-
- MOTH_BEGIN_INSTR(SetLookup)
- MOTH_END_INSTR(SetLookup)
-
- MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- MOTH_END_INSTR(StoreScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- MOTH_END_INSTR(LoadScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- MOTH_END_INSTR(StoreContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- MOTH_END_INSTR(LoadContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadIdObject)
- MOTH_END_INSTR(LoadIdObject)
-
- MOTH_BEGIN_INSTR(CallValue)
- MOTH_END_INSTR(CallValue)
-
- MOTH_BEGIN_INSTR(CallProperty)
- MOTH_END_INSTR(CallProperty)
-
- MOTH_BEGIN_INSTR(CallPropertyLookup)
- MOTH_END_INSTR(CallPropertyLookup)
-
- MOTH_BEGIN_INSTR(CallElement)
- MOTH_END_INSTR(CallElement)
-
- MOTH_BEGIN_INSTR(CallName)
- MOTH_END_INSTR(CallName)
-
- MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
- MOTH_END_INSTR(CallPossiblyDirectEval)
-
- MOTH_BEGIN_INSTR(CallGlobalLookup)
- MOTH_END_INSTR(CallGlobalLookup)
-
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
- MOTH_END_INSTR(CallContextObjectProperty)
-
- MOTH_BEGIN_INSTR(SetExceptionHandler)
- addLabel(code - start + offset);
- MOTH_END_INSTR(SetExceptionHandler)
-
- MOTH_BEGIN_INSTR(ThrowException)
- MOTH_END_INSTR(ThrowException)
-
- MOTH_BEGIN_INSTR(GetException)
- MOTH_END_INSTR(HasException)
-
- MOTH_BEGIN_INSTR(SetException)
- MOTH_END_INSTR(SetExceptionFlag)
-
- MOTH_BEGIN_INSTR(CreateCallContext)
- MOTH_END_INSTR(CreateCallContext)
-
- MOTH_BEGIN_INSTR(PushCatchContext)
- MOTH_END_INSTR(PushCatchContext)
-
- MOTH_BEGIN_INSTR(PushWithContext)
- MOTH_END_INSTR(PushWithContext)
-
- MOTH_BEGIN_INSTR(PopContext)
- MOTH_END_INSTR(PopContext)
-
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
- MOTH_END_INSTR(ForeachIteratorObject)
-
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
- MOTH_END_INSTR(ForeachNextPropertyName)
-
- MOTH_BEGIN_INSTR(DeleteMember)
- MOTH_END_INSTR(DeleteMember)
-
- MOTH_BEGIN_INSTR(DeleteSubscript)
- MOTH_END_INSTR(DeleteSubscript)
-
- MOTH_BEGIN_INSTR(DeleteName)
- MOTH_END_INSTR(DeleteName)
-
- MOTH_BEGIN_INSTR(TypeofName)
- MOTH_END_INSTR(TypeofName)
-
- MOTH_BEGIN_INSTR(TypeofValue)
- MOTH_END_INSTR(TypeofValue)
-
- MOTH_BEGIN_INSTR(DeclareVar)
- MOTH_END_INSTR(DeclareVar)
-
- MOTH_BEGIN_INSTR(DefineArray)
- MOTH_END_INSTR(DefineArray)
-
- MOTH_BEGIN_INSTR(DefineObjectLiteral)
- MOTH_END_INSTR(DefineObjectLiteral)
-
- MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
- MOTH_END_INSTR(CreateMappedArgumentsObject)
-
- MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
- MOTH_END_INSTR(CreateUnmappedArgumentsObject)
-
- MOTH_BEGIN_INSTR(ConvertThisToObject)
- MOTH_END_INSTR(ConvertThisToObject)
-
- MOTH_BEGIN_INSTR(Construct)
- MOTH_END_INSTR(Construct)
-
- MOTH_BEGIN_INSTR(Jump)
- addLabel(code - start + offset);
- MOTH_END_INSTR(Jump)
-
- MOTH_BEGIN_INSTR(JumpTrue)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpTrue)
-
- MOTH_BEGIN_INSTR(JumpFalse)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpFalse)
-
- MOTH_BEGIN_INSTR(CmpEqNull)
- MOTH_END_INSTR(CmpEqNull)
-
- MOTH_BEGIN_INSTR(CmpNeNull)
- MOTH_END_INSTR(CmpNeNull)
-
- MOTH_BEGIN_INSTR(CmpEqInt)
- MOTH_END_INSTR(CmpEq)
-
- MOTH_BEGIN_INSTR(CmpNeInt)
- MOTH_END_INSTR(CmpNeInt)
-
- MOTH_BEGIN_INSTR(CmpEq)
- MOTH_END_INSTR(CmpEq)
-
- MOTH_BEGIN_INSTR(CmpNe)
- MOTH_END_INSTR(CmpNe)
-
- MOTH_BEGIN_INSTR(CmpGt)
- MOTH_END_INSTR(CmpGt)
-
- MOTH_BEGIN_INSTR(CmpGe)
- MOTH_END_INSTR(CmpGe)
-
- MOTH_BEGIN_INSTR(CmpLt)
- MOTH_END_INSTR(CmpLt)
-
- MOTH_BEGIN_INSTR(CmpLe)
- MOTH_END_INSTR(CmpLe)
-
- MOTH_BEGIN_INSTR(CmpStrictEqual)
- MOTH_END_INSTR(CmpStrictEqual)
-
- MOTH_BEGIN_INSTR(CmpStrictNotEqual)
- MOTH_END_INSTR(CmpStrictNotEqual)
-
- MOTH_BEGIN_INSTR(CmpIn)
- MOTH_END_INSTR(CmpIn)
-
- MOTH_BEGIN_INSTR(CmpInstanceOf)
- MOTH_END_INSTR(CmpInstanceOf)
-
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(UNot)
- MOTH_END_INSTR(UNot)
-
- MOTH_BEGIN_INSTR(UPlus)
- MOTH_END_INSTR(UPlus)
-
- MOTH_BEGIN_INSTR(UMinus)
- MOTH_END_INSTR(UMinus)
-
- MOTH_BEGIN_INSTR(UCompl)
- MOTH_END_INSTR(UCompl)
-
- MOTH_BEGIN_INSTR(Increment)
- MOTH_END_INSTR(PreIncrement)
-
- MOTH_BEGIN_INSTR(Decrement)
- MOTH_END_INSTR(PreDecrement)
-
- MOTH_BEGIN_INSTR(Add)
- MOTH_END_INSTR(Add)
-
- MOTH_BEGIN_INSTR(BitAnd)
- MOTH_END_INSTR(BitAnd)
-
- MOTH_BEGIN_INSTR(BitOr)
- MOTH_END_INSTR(BitOr)
-
- MOTH_BEGIN_INSTR(BitXor)
- MOTH_END_INSTR(BitXor)
-
- MOTH_BEGIN_INSTR(UShr)
- MOTH_END_INSTR(UShr)
-
- MOTH_BEGIN_INSTR(Shr)
- MOTH_END_INSTR(Shr)
-
- MOTH_BEGIN_INSTR(Shl)
- MOTH_END_INSTR(Shl)
-
- MOTH_BEGIN_INSTR(BitAndConst)
- MOTH_END_INSTR(BitAndConst)
-
- MOTH_BEGIN_INSTR(BitOrConst)
- MOTH_END_INSTR(BitOr)
-
- MOTH_BEGIN_INSTR(BitXorConst)
- MOTH_END_INSTR(BitXor)
-
- MOTH_BEGIN_INSTR(UShrConst)
- MOTH_END_INSTR(UShrConst)
-
- MOTH_BEGIN_INSTR(ShrConst)
- MOTH_END_INSTR(ShrConst)
-
- MOTH_BEGIN_INSTR(ShlConst)
- MOTH_END_INSTR(ShlConst)
-
- MOTH_BEGIN_INSTR(Mul)
- MOTH_END_INSTR(Mul)
-
- MOTH_BEGIN_INSTR(Div)
- MOTH_END_INSTR(Div)
-
- MOTH_BEGIN_INSTR(Mod)
- MOTH_END_INSTR(Mod)
-
- MOTH_BEGIN_INSTR(Sub)
- MOTH_END_INSTR(Sub)
-
- MOTH_BEGIN_INSTR(Ret)
- MOTH_END_INSTR(Ret)
-
- MOTH_BEGIN_INSTR(Debug)
- MOTH_END_INSTR(Debug)
-
- MOTH_BEGIN_INSTR(LoadQmlContext)
- MOTH_END_INSTR(LoadQmlContext)
-
- MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- MOTH_END_INSTR(LoadQmlImportedScripts)
- }
-}
-#undef MOTH_BEGIN_INSTR
-#undef MOTH_END_INSTR
-
#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 5aebf78a8d..d96fd6ea6a 100644
--- a/src/qml/jit/qv4jit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -54,36 +54,10 @@
#include <private/qv4global_p.h>
#include <private/qv4function_p.h>
#include <private/qv4instr_moth_p.h>
+#include <private/qv4bytecodehandler_p.h>
//QT_REQUIRE_CONFIG(qml_jit);
-#define JIT_DEFINE_ARGS(nargs, ...) \
- MOTH_EXPAND_FOR_MSVC(JIT_DEFINE_ARGS##nargs(__VA_ARGS__))
-
-#define JIT_DEFINE_ARGS0()
-#define JIT_DEFINE_ARGS1(arg) \
- int arg
-#define JIT_DEFINE_ARGS2(arg1, arg2) \
- int arg1, \
- int arg2
-#define JIT_DEFINE_ARGS3(arg1, arg2, arg3) \
- int arg1, \
- int arg2, \
- int arg3
-#define JIT_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
- int arg1, \
- int arg2, \
- int arg3, \
- int arg4
-
-#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \
- virtual void generate_##name( \
- JIT_DEFINE_ARGS(nargs, __VA_ARGS__) \
- ) = 0;
-
-#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
- INSTR_##instr(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER)
-
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -91,31 +65,12 @@ namespace JIT {
class Assembler;
-class ByteCodeHandler
-{
-public:
- virtual ~ByteCodeHandler();
-
- void decode(const char *code, uint len);
-
- int instructionOffset() const { return _offset; }
-
-protected:
- FOR_EACH_MOTH_INSTR(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER)
-
- virtual void startInstruction(Moth::Instr::Type instr) = 0;
- virtual void endInstruction(Moth::Instr::Type instr) = 0;
-
-private:
- int _offset = 0;
-};
-
#ifdef V4_ENABLE_JIT
-class BaselineJIT final: public ByteCodeHandler
+class BaselineJIT final: public Moth::ByteCodeHandler
{
public:
BaselineJIT(QV4::Function *);
- virtual ~BaselineJIT();
+ virtual ~BaselineJIT() Q_DECL_OVERRIDE;
void generate();
@@ -143,15 +98,14 @@ public:
void generate_LoadGlobalLookup(int index) override;
void generate_StoreNameSloppy(int name) override;
void generate_StoreNameStrict(int name) override;
- void generate_LoadElement(int base, int index) override;
- void generate_LoadElementA(int base) override;
+ void generate_LoadElement(int base) override;
void generate_StoreElement(int base, int index) override;
- void generate_LoadProperty(int name, int base) override;
- void generate_LoadPropertyA(int name) override;
- void generate_GetLookup(int index, int base) override;
- void generate_GetLookupA(int index) override;
+ void generate_LoadProperty(int name) override;
+ void generate_GetLookup(int index) override;
void generate_StoreProperty(int name, int base) override;
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,
@@ -161,6 +115,9 @@ public:
void generate_LoadContextObjectProperty(int propertyIndex, int base,
int captureRequired) override;
void generate_LoadIdObject(int index, int base) override;
+ void generate_Yield() override;
+ void generate_Resume(int) override;
+
void generate_CallValue(int name, int argc, int argv) override;
void generate_CallProperty(int name, int base, int argc, int argv) override;
void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override;
@@ -170,33 +127,46 @@ public:
void generate_CallGlobalLookup(int index, int argc, int argv) override;
void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override;
void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override;
- void generate_SetExceptionHandler(int offset) override;
+ void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override;
+ void generate_Construct(int func, int argc, int argv) override;
+ void generate_ConstructWithSpread(int func, int argc, int argv) override;
+ void generate_SetUnwindHandler(int offset) override;
+ void generate_UnwindDispatch() override;
+ void generate_UnwindToLabel(int level, int offset) override;
void generate_ThrowException() override;
void generate_GetException() override;
void generate_SetException() override;
void generate_CreateCallContext() override;
- void generate_PushCatchContext(int name, int reg) override;
- void generate_PushWithContext(int reg) override;
- void generate_PopContext(int reg) override;
- void generate_ForeachIteratorObject() override;
- void generate_ForeachNextPropertyName() override;
- void generate_DeleteMember(int member, int base) override;
- void generate_DeleteSubscript(int base, int index) override;
+ void generate_PushCatchContext(int index, int name) override;
+ void generate_PushWithContext() override;
+ void generate_PushBlockContext(int index) override;
+ void generate_CloneBlockContext() override;
+ void generate_PushScriptContext(int index) override;
+ void generate_PopScriptContext() override;
+ void generate_PopContext() override;
+ void generate_GetIterator(int iterator) override;
+ void generate_IteratorNext(int value) override;
+ void generate_IteratorClose(int done) override;
+ void generate_DestructureRestElement() override;
+ void generate_DeleteProperty(int base, int index) override;
void generate_DeleteName(int name) override;
void generate_TypeofName(int name) override;
void generate_TypeofValue() override;
void generate_DeclareVar(int varName, int isDeletable) override;
void generate_DefineArray(int argc, int args) override;
- void generate_DefineObjectLiteral(int internalClassId, int arrayValueCount,
- int arrayGetterSetterCountAndFlags,
- int args) override;
+ void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override;
+ void generate_CreateClass(int classIndex, int heritage, int computedNames) override;
void generate_CreateMappedArgumentsObject() override;
void generate_CreateUnmappedArgumentsObject() override;
+ void generate_CreateRestParameter(int argIndex) override;
void generate_ConvertThisToObject() override;
- void generate_Construct(int func, int argc, int argv) override;
+ void generate_LoadSuperConstructor() override;
+ void generate_ToObject() override;
void generate_Jump(int offset) override;
void generate_JumpTrue(int offset) override;
void generate_JumpFalse(int offset) override;
+ void generate_JumpNoException(int offset) override;
+ void generate_JumpNotUndefined(int offset) override;
void generate_CmpEqNull() override;
void generate_CmpNeNull() override;
void generate_CmpEqInt(int lhs) override;
@@ -211,10 +181,6 @@ public:
void generate_CmpStrictNotEqual(int lhs) override;
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
- void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
- void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
void generate_UNot() override;
void generate_UPlus() override;
void generate_UMinus() override;
@@ -234,6 +200,7 @@ public:
void generate_UShrConst(int rhs) override;
void generate_ShrConst(int rhs) override;
void generate_ShlConst(int rhs) override;
+ void generate_Exp(int lhs) override;
void generate_Mul(int lhs) override;
void generate_Div(int lhs) override;
void generate_Mod(int lhs) override;
@@ -249,9 +216,6 @@ protected:
{ return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); }
private:
- void collectLabelsInBytecode();
-
-private:
QV4::Function *function;
QScopedPointer<Assembler> as;
std::vector<int> labels;
diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp
new file mode 100644
index 0000000000..23e3095a85
--- /dev/null
+++ b/src/qml/jit/qv4jithelpers.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4jithelpers_p.h"
+#include "qv4engine_p.h"
+#include "qv4function_p.h"
+#include "qv4value_p.h"
+#include "qv4object_p.h"
+#include "qv4lookup_p.h"
+#include <QtCore/private/qnumeric_p.h>
+
+#ifdef V4_ENABLE_JIT
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace JIT {
+namespace Helpers {
+
+void convertThisToObject(ExecutionEngine *engine, Value *t)
+{
+ if (!t->isObject()) {
+ if (t->isNullOrUndefined()) {
+ *t = engine->globalObject->asReturnedValue();
+ } else {
+ *t = t->toObject(engine)->asReturnedValue();
+ }
+ }
+}
+
+ReturnedValue loadGlobalLookup(ExecutionEngine *engine, Function *f, int index)
+{
+ Lookup *l = f->compilationUnit->runtimeLookups + index;
+ return l->globalGetter(l, engine);
+}
+
+ReturnedValue loadSuperConstructor(ExecutionEngine *engine, const Value *t)
+{
+ if (!t->isObject()) {
+ engine->throwTypeError();
+ return Encode::undefined();
+ }
+ return static_cast<const Object *>(t)->getPrototypeOf()->asReturnedValue();
+}
+
+ReturnedValue toObject(ExecutionEngine *engine, const Value &obj)
+{
+ if (obj.isObject())
+ return obj.asReturnedValue();
+
+ return obj.toObject(engine)->asReturnedValue();
+}
+
+ReturnedValue exp(const Value &base, const Value &exp)
+{
+ double b = base.toNumber();
+ double e = exp.toNumber();
+ if (qt_is_inf(e) && (b == 1 || b == -1))
+ return Encode(qt_snan());
+ return Encode(pow(b,e));
+}
+
+ReturnedValue getLookup(ExecutionEngine *engine, Function *f, int index, const Value &base)
+{
+ Lookup *l = f->compilationUnit->runtimeLookups + index;
+ return l->getter(l, engine, base);
+}
+
+void setLookup(Function *f, int index, Value &base, const Value &value)
+{
+ ExecutionEngine *engine = f->internalClass->engine;
+ QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
+ if (!l->setter(l, engine, base, value) && f->isStrict())
+ engine->throwTypeError();
+}
+
+void pushBlockContext(Value *stack, int index)
+{
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ stack[CallData::Context] = Runtime::method_createBlockContext(c, index);
+}
+
+void cloneBlockContext(Value *contextSlot)
+{
+ *contextSlot = Runtime::method_cloneBlockContext(static_cast<QV4::ExecutionContext *>(contextSlot));
+}
+
+void pushScriptContext(Value *stack, ExecutionEngine *engine, int index)
+{
+ stack[CallData::Context] = Runtime::method_createScriptContext(engine, index);
+}
+
+void popScriptContext(Value *stack, ExecutionEngine *engine)
+{
+ stack[CallData::Context] = Runtime::method_popScriptContext(engine);
+}
+
+ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
+{
+ auto engine = function->internalClass->engine;
+ if (!Runtime::method_deleteProperty(engine, base, index)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+ReturnedValue deleteName(Function *function, int name)
+{
+ auto engine = function->internalClass->engine;
+ if (!Runtime::method_deleteName(engine, name)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+} // Helpers namespace
+} // JIT namespace
+} // QV4 namespace
+QT_END_NAMESPACE
+
+#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/jit/qv4jithelpers_p.h
new file mode 100644
index 0000000000..e0dfdc47d9
--- /dev/null
+++ b/src/qml/jit/qv4jithelpers_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TEMPLATE_H
+#define TEMPLATE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+
+//QT_REQUIRE_CONFIG(qml_jit);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+#ifdef V4_ENABLE_JIT
+
+namespace JIT {
+namespace Helpers {
+
+void convertThisToObject(ExecutionEngine *engine, Value *t);
+ReturnedValue loadGlobalLookup(ExecutionEngine *engine, Function *f, int index);
+ReturnedValue loadSuperConstructor(ExecutionEngine *engine, const Value *t);
+ReturnedValue toObject(ExecutionEngine *engine, const Value &obj);
+ReturnedValue exp(const Value &base, const Value &exp);
+ReturnedValue getLookup(ExecutionEngine *engine, Function *f, int index, const Value &base);
+void setLookup(Function *f, int index, Value &base, const Value &value);
+void pushBlockContext(Value *stack, int index);
+void cloneBlockContext(Value *contextSlot);
+void pushScriptContext(Value *stack, ExecutionEngine *engine, int index);
+void popScriptContext(Value *stack, ExecutionEngine *engine);
+ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index);
+ReturnedValue deleteName(Function *function, int name);
+
+} // Helpers namespace
+} // JIT namespace
+
+#endif // V4_ENABLE_JIT
+
+} // QV4 namespace
+
+QT_END_NAMESPACE
+
+#endif // TEMPLATE_H
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index c483af638b..5fa81ccc2a 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -51,6 +51,7 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4stackframe_p.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qmetaobject.h>
@@ -304,9 +305,9 @@ QJSEngine::QJSEngine()
QJSEngine::QJSEngine(QObject *parent)
: QObject(*new QJSEnginePrivate, parent)
- , m_v4Engine(new QV4::ExecutionEngine)
+ , m_v4Engine(new QV4::ExecutionEngine(this))
{
- m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine);
+ m_v4Engine->v8Engine = new QV8Engine(m_v4Engine);
checkForApplicationInstance();
QJSEnginePrivate::addToDebugServer(this);
@@ -317,9 +318,9 @@ QJSEngine::QJSEngine(QObject *parent)
*/
QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
: QObject(dd, parent)
- , m_v4Engine(new QV4::ExecutionEngine)
+ , m_v4Engine(new QV4::ExecutionEngine(this))
{
- m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine);
+ m_v4Engine->v8Engine = new QV8Engine(m_v4Engine);
checkForApplicationInstance();
}
@@ -462,7 +463,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
QV4::Scope scope(v4);
QV4::ScopedValue result(scope);
- QV4::Script script(v4->rootContext(), QV4::Compiler::GlobalCode, program, fileName, lineNumber);
+ QV4::Script script(v4->rootContext(), QV4::Compiler::ContextType::Global, program, fileName, lineNumber);
script.strictMode = false;
if (v4->currentStackFrame)
script.strictMode = v4->currentStackFrame->v4Function->isStrict();
@@ -738,6 +739,74 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
\sa toScriptValue()
*/
+/*!
+ Throws a run-time error (exception) with the given \a message.
+
+ This method is the C++ counterpart of a \c throw() expression in
+ JavaScript. It enables C++ code to report run-time errors to QJSEngine.
+ Therefore it should only be called from C++ code that was invoked by a
+ JavaScript function through QJSEngine.
+
+ When returning from C++, the engine will interrupt the normal flow of
+ execution and call the the next pre-registered exception handler with
+ an error object that contains the given \a message. The error object
+ will point to the location of the top-most context on the JavaScript
+ caller stack; specifically, it will have properties \c lineNumber,
+ \c fileName and \c stack. These properties are described in
+ \l{Script Exceptions}.
+
+ In the following example a C++ method in \e FileAccess.cpp throws an error
+ in \e qmlFile.qml at the position where \c readFileAsText() is called:
+
+ \code
+ // qmlFile.qml
+ function someFunction() {
+ ...
+ var text = FileAccess.readFileAsText("/path/to/file.txt");
+ }
+ \endcode
+
+ \code
+ // FileAccess.cpp
+ // Assuming that FileAccess is a QObject-derived class that has been
+ // registered as a singleton type and provides an invokable method
+ // readFileAsText()
+
+ QJSValue FileAccess::readFileAsText(const QString & filePath) {
+ QFile file(filePath);
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ jsEngine->throwError(file.errorString());
+ return QString();
+ }
+
+ ...
+ return content;
+ }
+ \endcode
+
+ It is also possible to catch the thrown error in JavaScript:
+ \code
+ // qmlFile.qml
+ function someFunction() {
+ ...
+ var text;
+ try {
+ text = FileAccess.readFileAsText("/path/to/file.txt");
+ } catch (error) {
+ console.warn("In " + error.fileName + ":" + "error.lineNumber" +
+ ": " + error.message);
+ }
+ }
+ \endcode
+
+ \since Qt 5.12
+ \sa {Script Exceptions}
+*/
+void QJSEngine::throwError(const QString &message)
+{
+ m_v4Engine->throwError(message);
+}
QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e)
{
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index 3ba2b52e89..36a3e475f2 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -111,6 +111,8 @@ public:
QV4::ExecutionEngine *handle() const { return m_v4Engine; }
+ void throwError(const QString &message);
+
private:
QJSValue create(int type, const void *ptr);
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 348ddb25d9..bf8000bdd8 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -852,7 +852,7 @@ QJSValue QJSValue::prototype() const
ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<QV4::Object>());
if (!o)
return QJSValue();
- ScopedObject p(scope, o->prototype());
+ ScopedObject p(scope, o->getPrototypeOf());
if (!p)
return QJSValue(NullValue);
return QJSValue(o->internalClass()->engine, p.asReturnedValue());
@@ -884,7 +884,7 @@ void QJSValue::setPrototype(const QJSValue& prototype)
if (!val)
return;
if (val->isNull()) {
- o->setPrototype(nullptr);
+ o->setPrototypeOf(nullptr);
return;
}
@@ -895,7 +895,7 @@ void QJSValue::setPrototype(const QJSValue& prototype)
qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine");
return;
}
- if (!o->setPrototype(p))
+ if (!o->setPrototypeOf(p))
qWarning("QJSValue::setPrototype() failed: cyclic prototype value");
}
@@ -1058,12 +1058,7 @@ QJSValue QJSValue::property(const QString& name) const
return QJSValue();
ScopedString s(scope, engine->newString(name));
- uint idx = s->asArrayIndex();
- if (idx < UINT_MAX)
- return property(idx);
-
- s->makeIdentifier();
- QV4::ScopedValue result(scope, o->get(s));
+ QV4::ScopedValue result(scope, o->get(s->toPropertyKey()));
if (engine->hasException)
result = engine->catchException();
@@ -1110,7 +1105,7 @@ QJSValue QJSValue::property(quint32 arrayIndex) const
if (!o)
return QJSValue();
- QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->getIndexed(arrayIndex));
+ QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->get(arrayIndex));
if (engine->hasException)
engine->catchException();
return QJSValue(engine, result->asReturnedValue());
@@ -1148,15 +1143,8 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value)
}
ScopedString s(scope, engine->newString(name));
- uint idx = s->asArrayIndex();
- if (idx < UINT_MAX) {
- setProperty(idx, value);
- return;
- }
-
- s->makeIdentifier();
QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
- o->put(s, v);
+ o->put(s->toPropertyKey(), v);
if (engine->hasException)
engine->catchException();
}
@@ -1209,10 +1197,8 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value)
}
QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
- if (arrayIndex != UINT_MAX)
- o->putIndexed(arrayIndex, v);
- else
- o->put(engine->id_uintMax(), v);
+ PropertyKey id = arrayIndex != UINT_MAX ? PropertyKey::fromArrayIndex(arrayIndex) : engine->id_uintMax()->propertyKey();
+ o->put(id, v);
if (engine->hasException)
engine->catchException();
}
@@ -1249,7 +1235,7 @@ bool QJSValue::deleteProperty(const QString &name)
return false;
ScopedString s(scope, engine->newString(name));
- return o->deleteProperty(s);
+ return o->deleteProperty(s->toPropertyKey());
}
/*!
@@ -1269,8 +1255,8 @@ bool QJSValue::hasProperty(const QString &name) const
if (!o)
return false;
- ScopedString s(scope, engine->newIdentifier(name));
- return o->hasProperty(s);
+ ScopedString s(scope, engine->newString(name));
+ return o->hasProperty(s->toPropertyKey());
}
/*!
@@ -1291,7 +1277,7 @@ bool QJSValue::hasOwnProperty(const QString &name) const
return false;
ScopedString s(scope, engine->newIdentifier(name));
- return o->hasOwnProperty(s);
+ return o->getOwnProperty(s->propertyKey()) != Attr_Invalid;
}
/*!
diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h
index 62e09f72be..bcf0a9d12d 100644
--- a/src/qml/jsapi/qjsvalue_p.h
+++ b/src/qml/jsapi/qjsvalue_p.h
@@ -62,7 +62,7 @@
QT_BEGIN_NAMESPACE
-class QJSValuePrivate
+class Q_AUTOTEST_EXPORT QJSValuePrivate
{
public:
static inline QV4::Value *getValue(const QJSValue *jsval)
diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp
index ce472ce7e5..35c6ecc668 100644
--- a/src/qml/jsapi/qjsvalueiterator.cpp
+++ b/src/qml/jsapi/qjsvalueiterator.cpp
@@ -57,7 +57,7 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v)
QV4::Scope scope(e);
QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&v));
- iterator.set(e, e->newForEachIteratorObject(o));
+ iterator.set(e, e->newForInIteratorObject(o));
}
@@ -102,7 +102,7 @@ QJSValueIterator::QJSValueIterator(const QJSValue& object)
if (!v4)
return;
QV4::Scope scope(v4);
- QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
+ QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value());
it->d()->it().flags = QV4::ObjectIterator::NoFlags;
QV4::ScopedString nm(scope);
QV4::Property nextProperty;
@@ -153,7 +153,7 @@ bool QJSValueIterator::next()
if (!v4)
return false;
QV4::Scope scope(v4);
- QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
+ QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value());
QV4::ScopedString nm(scope);
QV4::Property nextProperty;
QV4::PropertyAttributes nextAttributes;
@@ -200,7 +200,7 @@ QJSValue QJSValueIterator::value() const
if (!d_ptr->currentName.as<QV4::String>() && d_ptr->currentIndex == UINT_MAX)
return QJSValue();
- QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->getIndexed(d_ptr->currentIndex));
+ QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->get(d_ptr->currentIndex));
if (scope.hasException()) {
engine->catchException();
return QJSValue();
@@ -229,8 +229,8 @@ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object)
QV4::Scope scope(v4);
QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&object));
- d_ptr->iterator.set(v4, v4->newForEachIteratorObject(o));
- QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
+ d_ptr->iterator.set(v4, v4->newForInIteratorObject(o));
+ QV4::Scoped<QV4::ForInIteratorObject> it(scope, d_ptr->iterator.value());
it->d()->it().flags = QV4::ObjectIterator::NoFlags;
QV4::ScopedString nm(scope);
QV4::Property nextProperty;
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 4bc877bd9d..ec5803b2df 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -14,21 +14,29 @@ SOURCES += \
$$PWD/qv4sparsearray.cpp \
$$PWD/qv4arraydata.cpp \
$$PWD/qv4arrayobject.cpp \
+ $$PWD/qv4arrayiterator.cpp \
$$PWD/qv4argumentsobject.cpp \
$$PWD/qv4booleanobject.cpp \
$$PWD/qv4dateobject.cpp \
$$PWD/qv4errorobject.cpp \
$$PWD/qv4function.cpp \
$$PWD/qv4functionobject.cpp \
+ $$PWD/qv4generatorobject.cpp \
$$PWD/qv4globalobject.cpp \
+ $$PWD/qv4iterator.cpp \
$$PWD/qv4jsonobject.cpp \
$$PWD/qv4mathobject.cpp \
$$PWD/qv4memberdata.cpp \
$$PWD/qv4numberobject.cpp \
$$PWD/qv4object.cpp \
$$PWD/qv4objectproto.cpp \
+ $$PWD/qv4propertykey.cpp \
+ $$PWD/qv4proxy.cpp \
$$PWD/qv4qmlcontext.cpp \
+ $$PWD/qv4reflect.cpp \
$$PWD/qv4regexpobject.cpp \
+ $$PWD/qv4stackframe.cpp \
+ $$PWD/qv4stringiterator.cpp \
$$PWD/qv4stringobject.cpp \
$$PWD/qv4variantobject.cpp \
$$PWD/qv4objectiterator.cpp \
@@ -36,13 +44,18 @@ SOURCES += \
$$PWD/qv4runtimecodegen.cpp \
$$PWD/qv4serialize.cpp \
$$PWD/qv4script.cpp \
- $$PWD/qv4sequenceobject.cpp \
+ $$PWD/qv4symbol.cpp \
+ $$PWD/qv4setobject.cpp \
+ $$PWD/qv4setiterator.cpp \
$$PWD/qv4include.cpp \
$$PWD/qv4qobjectwrapper.cpp \
$$PWD/qv4arraybuffer.cpp \
$$PWD/qv4typedarray.cpp \
$$PWD/qv4dataview.cpp \
- $$PWD/qv4vme_moth.cpp
+ $$PWD/qv4vme_moth.cpp \
+ $$PWD/qv4mapobject.cpp \
+ $$PWD/qv4mapiterator.cpp \
+ $$PWD/qv4estable.cpp
qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp
@@ -64,22 +77,30 @@ HEADERS += \
$$PWD/qv4sparsearray_p.h \
$$PWD/qv4arraydata_p.h \
$$PWD/qv4arrayobject_p.h \
+ $$PWD/qv4arrayiterator_p.h \
$$PWD/qv4argumentsobject_p.h \
$$PWD/qv4booleanobject_p.h \
$$PWD/qv4dateobject_p.h \
$$PWD/qv4errorobject_p.h \
$$PWD/qv4function_p.h \
$$PWD/qv4functionobject_p.h \
+ $$PWD/qv4generatorobject_p.h \
$$PWD/qv4globalobject_p.h \
+ $$PWD/qv4iterator_p.h \
$$PWD/qv4jsonobject_p.h \
$$PWD/qv4mathobject_p.h \
$$PWD/qv4memberdata_p.h \
$$PWD/qv4numberobject_p.h \
$$PWD/qv4object_p.h \
$$PWD/qv4objectproto_p.h \
+ $$PWD/qv4propertykey_p.h \
+ $$PWD/qv4proxy_p.h \
$$PWD/qv4qmlcontext_p.h \
+ $$PWD/qv4reflect_p.h \
$$PWD/qv4regexpobject_p.h \
$$PWD/qv4runtimecodegen_p.h \
+ $$PWD/qv4stackframe_p.h \
+ $$PWD/qv4stringiterator_p.h \
$$PWD/qv4stringobject_p.h \
$$PWD/qv4variantobject_p.h \
$$PWD/qv4property_p.h \
@@ -87,16 +108,30 @@ HEADERS += \
$$PWD/qv4regexp_p.h \
$$PWD/qv4serialize_p.h \
$$PWD/qv4script_p.h \
+ $$PWD/qv4symbol_p.h \
+ $$PWD/qv4setobject_p.h \
+ $$PWD/qv4setiterator_p.h \
$$PWD/qv4scopedvalue_p.h \
$$PWD/qv4executableallocator_p.h \
- $$PWD/qv4sequenceobject_p.h \
$$PWD/qv4include_p.h \
$$PWD/qv4qobjectwrapper_p.h \
$$PWD/qv4profiling_p.h \
$$PWD/qv4arraybuffer_p.h \
$$PWD/qv4typedarray_p.h \
$$PWD/qv4dataview_p.h \
- $$PWD/qv4vme_moth_p.h
+ $$PWD/qv4vme_moth_p.h \
+ $$PWD/qv4mapobject_p.h \
+ $$PWD/qv4mapiterator_p.h \
+ $$PWD/qv4estable_p.h \
+ $$PWD/qv4vtable_p.h
+
+qtConfig(qml-sequence-object) {
+ HEADERS += \
+ $$PWD/qv4sequenceobject_p.h
+
+ SOURCES += \
+ $$PWD/qv4sequenceobject.cpp
+}
}
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 075e7afd8a..7b501b9fbb 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -37,11 +37,13 @@
**
****************************************************************************/
#include <qv4argumentsobject_p.h>
+#include <qv4arrayobject_p.h>
#include <qv4alloca_p.h>
#include <qv4scopedvalue_p.h>
#include <qv4string_p.h>
#include <qv4function_p.h>
#include <qv4jscall_p.h>
+#include <qv4symbol_p.h>
using namespace QV4;
@@ -61,10 +63,12 @@ void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame)
this->context.set(v4, context->d());
Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable());
- Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
+ Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->propertyKey()));
setProperty(v4, CalleePropertyIndex, context->d()->function);
- Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()));
+ Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()->propertyKey()));
setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc()));
+ Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->propertyKey()));
+ setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues());
}
void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame)
@@ -74,19 +78,18 @@ void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame)
Object::init();
- Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
- Q_ASSERT(CallerPropertyIndex == internalClass->find(v4->id_caller()));
+ Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->propertyKey()));
+ Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->propertyKey()));
+ setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues());
setProperty(v4, CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
setProperty(v4, CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
- setProperty(v4, CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
- setProperty(v4, CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
Scope scope(v4);
Scoped<QV4::StrictArgumentsObject> args(scope, this);
args->arrayReserve(frame->originalArgumentsCount);
args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount);
- Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()));
+ Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()->propertyKey()));
setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount));
}
@@ -117,106 +120,111 @@ void ArgumentsObject::fullyCreate()
d()->fullyCreated = true;
}
-bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs)
+bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs)
{
- fullyCreate();
+ if (!id.isArrayIndex())
+ return Object::virtualDefineOwnProperty(m, id, desc, attrs);
- Scope scope(engine);
+ ArgumentsObject *a = static_cast<ArgumentsObject *>(m);
+ a->fullyCreate();
+
+ uint index = id.asArrayIndex();
+ Scope scope(m);
ScopedProperty map(scope);
PropertyAttributes mapAttrs;
- uint numAccessors = qMin(d()->nFormals, context()->argc());
+ uint numAccessors = qMin(a->d()->nFormals, a->context()->argc());
bool isMapped = false;
- if (arrayData() && index < numAccessors &&
- arrayData()->attributes(index).isAccessor() &&
- arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue())
+ if (a->arrayData() && index < numAccessors &&
+ a->arrayData()->attributes(index).isAccessor() &&
+ a->arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue())
isMapped = true;
if (isMapped) {
- Q_ASSERT(arrayData());
- mapAttrs = arrayData()->attributes(index);
- arrayData()->getProperty(index, map, &mapAttrs);
- setArrayAttributes(index, Attr_Data);
- ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) };
- arrayIndex.set(scope.engine, d()->mappedArguments->values[index]);
+ Q_ASSERT(a->arrayData());
+ mapAttrs = a->arrayData()->attributes(index);
+ a->arrayData()->getProperty(index, map, &mapAttrs);
+ a->setArrayAttributes(index, Attr_Data);
+ PropertyIndex arrayIndex{ a->arrayData(), a->arrayData()->values.values + a->arrayData()->mappedIndex(index) };
+ arrayIndex.set(scope.engine, a->d()->mappedArguments->values[index]);
}
- bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs);
- if (!result) {
+ bool result = Object::virtualDefineOwnProperty(m, id, desc, attrs);
+ if (!result)
return false;
- }
if (isMapped && attrs.isData()) {
- Q_ASSERT(arrayData());
+ Q_ASSERT(a->arrayData());
ScopedFunctionObject setter(scope, map->setter());
JSCallData jsCallData(scope, 1);
- *jsCallData->thisObject = this->asReturnedValue();
+ *jsCallData->thisObject = a->asReturnedValue();
jsCallData->args[0] = desc->value;
setter->call(jsCallData);
if (attrs.isWritable()) {
- setArrayAttributes(index, mapAttrs);
- arrayData()->setProperty(engine, index, map);
+ a->setArrayAttributes(index, mapAttrs);
+ a->arrayData()->setProperty(m->engine(), index, map);
}
}
return result;
}
-ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
- if (args->fullyCreated())
- return Object::getIndexed(m, index, hasProperty);
-
- if (index < static_cast<uint>(args->context()->argc())) {
- if (hasProperty)
- *hasProperty = true;
- return args->context()->args()[index].asReturnedValue();
+ if (id.isArrayIndex() && !args->fullyCreated()) {
+ uint index = id.asArrayIndex();
+ if (index < static_cast<uint>(args->context()->argc())) {
+ if (hasProperty)
+ *hasProperty = true;
+ return args->context()->args()[index].asReturnedValue();
+ }
}
- if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
-bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value)
+bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
- if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc()))
- args->fullyCreate();
-
- if (args->fullyCreated())
- return Object::putIndexed(m, index, value);
-
- args->context()->setArg(index, value);
- return true;
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc()))
+ args->fullyCreate();
+
+ if (!args->fullyCreated()) {
+ args->context()->setArg(index, value);
+ return true;
+ }
+ }
+ return Object::virtualPut(m, id, value, receiver);
}
-bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index)
+bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id)
{
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
if (!args->fullyCreated())
args->fullyCreate();
- return Object::deleteIndexedProperty(m, index);
+ return Object::virtualDeleteProperty(m, id);
}
-PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index)
+PropertyAttributes ArgumentsObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
{
const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
- if (args->fullyCreated())
- return Object::queryIndexed(m, index);
+ if (!id.isArrayIndex() || args->fullyCreated())
+ return Object::virtualGetOwnProperty(m, id, p);
- uint numAccessors = qMin(args->d()->nFormals, args->context()->argc());
+ uint index = id.asArrayIndex();
uint argCount = args->context()->argc();
if (index >= argCount)
return PropertyAttributes();
- if (index >= numAccessors)
- return Attr_Data;
- return Attr_Accessor;
+ if (p)
+ p->value = args->context()->args()[index];
+ return Attr_Data;
}
DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction);
-ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const Value *thisObject, const Value *, int)
+ReturnedValue ArgumentsGetterFunction::virtualCall(const FunctionObject *getter, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = getter->engine();
Scope scope(v4);
@@ -231,7 +239,7 @@ ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const
DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction);
-ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue ArgumentsSetterFunction::virtualCall(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *v4 = setter->engine();
Scope scope(v4);
@@ -245,10 +253,8 @@ ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const
return Encode::undefined();
}
-uint ArgumentsObject::getLength(const Managed *m)
+qint64 ArgumentsObject::virtualGetLength(const Managed *m)
{
const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m);
- if (a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->isInteger())
- return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->integerValue();
- return Primitive::toUInt32(a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->doubleValue());
+ return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength();
}
diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h
index ac281f555a..9ac9ac5d8b 100644
--- a/src/qml/jsruntime/qv4argumentsobject_p.h
+++ b/src/qml/jsruntime/qv4argumentsobject_p.h
@@ -85,7 +85,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) {
DECLARE_MARKOBJECTS(ArgumentsObject);
enum {
LengthPropertyIndex = 0,
- CalleePropertyIndex = 1
+ SymbolIteratorPropertyIndex = 1,
+ CalleePropertyIndex = 2
};
void init(CppStackFrame *frame);
};
@@ -95,8 +96,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) {
DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) {
enum {
LengthPropertyIndex = 0,
- CalleePropertyIndex = 1,
- CallerPropertyIndex = 3
+ SymbolIteratorPropertyIndex = 1,
+ CalleePropertyIndex = 2
};
void init(CppStackFrame *frame);
};
@@ -108,7 +109,7 @@ struct ArgumentsGetterFunction: FunctionObject
V4_OBJECT2(ArgumentsGetterFunction, FunctionObject)
uint index() const { return d()->index; }
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
inline void
@@ -123,7 +124,7 @@ struct ArgumentsSetterFunction: FunctionObject
V4_OBJECT2(ArgumentsSetterFunction, FunctionObject)
uint index() const { return d()->index; }
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
inline void
@@ -142,15 +143,15 @@ struct ArgumentsObject: Object {
bool fullyCreated() const { return d()->fullyCreated; }
static bool isNonStrictArgumentsObject(Managed *m) {
- return m->d()->vtable() == staticVTable();
+ return m->vtable() == staticVTable();
}
- bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool putIndexed(Managed *m, uint index, const Value &value);
- static bool deleteIndexedProperty(Managed *m, uint index);
- static PropertyAttributes queryIndexed(const Managed *m, uint index);
- static uint getLength(const Managed *m);
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs);
+ 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 bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
+ static qint64 virtualGetLength(const Managed *m);
void fullyCreate();
diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp
index 59a2b9d913..f80c9a0ab2 100644
--- a/src/qml/jsruntime/qv4arraybuffer.cpp
+++ b/src/qml/jsruntime/qv4arraybuffer.cpp
@@ -41,6 +41,7 @@
#include "qv4dataview_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
using namespace QV4;
@@ -52,7 +53,7 @@ void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer"));
}
-ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
ExecutionEngine *v4 = f->engine();
Scope scope(v4);
@@ -73,9 +74,9 @@ ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const
}
-ReturnedValue ArrayBufferCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue ArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -94,7 +95,8 @@ ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value
void Heap::ArrayBuffer::init(size_t length)
{
Object::init();
- data = QTypedArrayData<char>::allocate(length + 1);
+ if (length < UINT_MAX)
+ data = QTypedArrayData<char>::allocate(length + 1);
if (!data) {
internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory"));
return;
@@ -147,13 +149,17 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1);
+ ctor->addSymbolSpecies();
+
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
+ ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer")));
+ defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
}
ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index 59e78ee85f..089dbc522f 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -78,8 +78,8 @@ struct ArrayBufferCtor: FunctionObject
{
V4_OBJECT2(ArrayBufferCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 855407e6f7..ce1d0503df 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -47,25 +47,7 @@
using namespace QV4;
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON
-
-const QV4::VTable QV4::ArrayData::static_vtbl = {
- nullptr,
- 0,
- 0,
- QV4::ArrayData::IsExecutionContext,
- QV4::ArrayData::IsString,
- QV4::ArrayData::IsObject,
- QV4::ArrayData::IsFunctionObject,
- QV4::ArrayData::IsErrorObject,
- QV4::ArrayData::IsArrayData,
- 0,
- QV4::ArrayData::MyType,
- "ArrayData",
- Q_VTABLE_FUNCTION(QV4::ArrayData, destroy),
- ArrayData::Data::markObjects,
- isEqualTo
-};
+DEFINE_MANAGED_VTABLE(ArrayData);
const ArrayVTable SimpleArrayData::static_vtbl =
{
@@ -99,18 +81,9 @@ const ArrayVTable SparseArrayData::static_vtbl =
SparseArrayData::length
};
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SimpleArrayData));
Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData));
-static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value)
-{
- Value v;
- v.setEmpty(value);
- *target = v.asReturnedValue();
-}
-
void Heap::ArrayData::markObjects(Heap::Base *base, MarkStack *stack)
{
ArrayData *a = static_cast<ArrayData *>(base);
@@ -195,7 +168,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
Heap::SparseArrayData *sparse = static_cast<Heap::SparseArrayData *>(newData->d());
- ReturnedValue *lastFree;
+ Value *lastFree;
if (d && d->type() == Heap::ArrayData::Sparse) {
Heap::SparseArrayData *old = static_cast<Heap::SparseArrayData *>(d->d());
sparse->sparse = old->sparse;
@@ -204,29 +177,29 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
} else {
sparse->sparse = new SparseArray;
lastFree = &sparse->sparse->freeList;
- storeValue(lastFree, 0);
+ *lastFree = Encode(0);
for (uint i = 0; i < toCopy; ++i) {
if (!sparse->values[i].isEmpty()) {
SparseArrayNode *n = sparse->sparse->insert(i);
n->value = i;
} else {
- storeValue(lastFree, i);
+ *lastFree = Encode(i);
sparse->values.values[i].setEmpty();
- lastFree = &sparse->values.values[i].rawValueRef();
+ lastFree = &sparse->values.values[i];
}
}
}
if (toCopy < sparse->values.alloc) {
for (uint i = toCopy; i < sparse->values.alloc; ++i) {
- storeValue(lastFree, i);
+ *lastFree = Encode(i);
sparse->values.values[i].setEmpty();
- lastFree = &sparse->values.values[i].rawValueRef();
+ lastFree = &sparse->values.values[i];
}
}
- storeValue(lastFree, UINT_MAX);
+ *lastFree = Encode(-1);
- Q_ASSERT(Value::fromReturnedValue(sparse->sparse->freeList).isEmpty());
+ Q_ASSERT(sparse->sparse->freeList.isInteger());
// ### Could explicitly free the old data
}
@@ -368,12 +341,12 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx)
Value *v = d->values.values + idx;
if (d->attrs && d->attrs[idx].isAccessor()) {
// double slot, free both. Order is important, so we have a double slot for allocation again afterwards.
- v[1].setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue());
- v[0].setEmpty(idx + 1);
+ v[1] = d->sparse->freeList;
+ v[0] = Encode(idx + 1);
} else {
- v->setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue());
+ *v = d->sparse->freeList;
}
- d->sparse->freeList = Primitive::emptyValue(idx).asReturnedValue();
+ d->sparse->freeList = Encode(idx);
if (d->attrs)
d->attrs[idx].clear();
}
@@ -390,36 +363,34 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot)
Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Sparse);
Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
if (doubleSlot) {
- ReturnedValue *last = &dd->sparse->freeList;
+ Value *last = &dd->sparse->freeList;
while (1) {
- if (Value::fromReturnedValue(*last).value() == UINT_MAX) {
+ if (last->int_32() == -1) {
reallocate(o, dd->values.alloc + 2, true);
dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
last = &dd->sparse->freeList;
- Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX);
+ Q_ASSERT(last->int_32() != -1);
}
- Q_ASSERT(dd->values[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value());
- if (dd->values[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) {
+ Q_ASSERT(dd->values[static_cast<uint>(last->int_32())].int_32() != last->int_32());
+ if (dd->values[static_cast<uint>(last->int_32())].int_32() == last->int_32() + 1) {
// found two slots in a row
- uint idx = Value::fromReturnedValue(*last).emptyValue();
- Value lastV = Value::fromReturnedValue(*last);
- lastV.setEmpty(dd->values[lastV.emptyValue() + 1].value());
- *last = lastV.rawValue();
+ uint idx = static_cast<uint>(last->int_32());
+ *last = Encode(dd->values[static_cast<uint>(last->int_32()) + 1].int_32());
dd->attrs[idx] = Attr_Accessor;
return idx;
}
- last = &dd->values.values[Value::fromReturnedValue(*last).value()].rawValueRef();
+ last = &dd->values.values[last->int_32()];
}
} else {
- if (Value::fromReturnedValue(dd->sparse->freeList).value() == UINT_MAX) {
+ if (dd->sparse->freeList.int_32() == -1) {
reallocate(o, dd->values.alloc + 1, false);
dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
}
- uint idx = Value::fromReturnedValue(dd->sparse->freeList).value();
- Q_ASSERT(idx != UINT_MAX);
- dd->sparse->freeList = dd->values[idx].asReturnedValue();
- Q_ASSERT(Value::fromReturnedValue(dd->sparse->freeList).isEmpty());
+ Q_ASSERT(dd->sparse->freeList.int_32() != -1);
+ uint idx = static_cast<uint>(dd->sparse->freeList.int_32());
+ dd->sparse->freeList = dd->values[idx];
+ Q_ASSERT(dd->sparse->freeList.isInteger());
if (dd->attrs)
dd->attrs[idx] = Attr_Data;
return idx;
@@ -474,14 +445,14 @@ bool SparseArrayData::del(Object *o, uint index)
if (isAccessor) {
// free up both indices
- dd->values.values[pidx + 1].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue());
- dd->values.values[pidx].setEmpty(pidx + 1);
+ dd->values.values[pidx + 1] = dd->sparse->freeList;
+ dd->values.values[pidx] = Encode(pidx + 1);
} else {
Q_ASSERT(dd->type == Heap::ArrayData::Sparse);
- dd->values.values[pidx].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue());
+ dd->values.values[pidx] = dd->sparse->freeList;
}
- dd->sparse->freeList = Primitive::emptyValue(pidx).asReturnedValue();
+ dd->sparse->freeList = Encode(pidx);
dd->sparse->erase(n);
return true;
}
@@ -593,7 +564,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n)
if (!other || ArgumentsObject::isNonStrictArgumentsObject(otherObj)) {
ScopedValue v(scope);
for (uint i = 0; i < n; ++i)
- obj->arraySet(oldSize + i, (v = otherObj->getIndexed(i)));
+ obj->arraySet(oldSize + i, (v = otherObj->get(i)));
} else if (other && other->isSparse()) {
Heap::SparseArrayData *os = static_cast<Heap::SparseArrayData *>(other->d());
if (other->hasAttributes()) {
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index 7ec060f9c6..ac5b430356 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -103,28 +103,16 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) {
enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 };
- struct Index {
- Heap::ArrayData *arrayData;
- uint index;
-
- void set(EngineBase *e, Value newVal) {
- arrayData->values.set(e, index, newVal);
- }
- const Value *operator->() const { return &arrayData->values[index]; }
- const Value &operator*() const { return arrayData->values[index]; }
- bool isNull() const { return !arrayData; }
- };
-
bool isSparse() const { return type == Sparse; }
- const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); }
+ const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(internalClass->vtable); }
inline ReturnedValue get(uint i) const {
return vtable()->get(this, i);
}
inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs);
inline void setProperty(EngineBase *e, uint index, const Property *p);
- inline Index getValueOrSetter(uint index, PropertyAttributes *attrs);
+ inline PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs);
inline PropertyAttributes attributes(uint i) const;
bool isEmpty(uint i) const {
@@ -187,8 +175,6 @@ struct Q_QML_EXPORT ArrayData : public Managed
IsArrayData = true
};
- typedef Heap::ArrayData::Index Index;
-
uint alloc() const { return d()->values.alloc; }
uint &alloc() { return d()->values.alloc; }
void setAlloc(uint a) { d()->values.alloc = a; }
@@ -303,9 +289,9 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs)
*attrs = attributes(index);
if (p) {
- p->value = *(Index{ this, mapped });
+ p->value = *(PropertyIndex{ this, values.values + mapped });
if (attrs->isAccessor())
- p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ });
+ p->set = *(PropertyIndex{ this, values.values + mapped + 1 /*Object::SetterOffset*/ });
}
return true;
}
@@ -326,16 +312,18 @@ inline PropertyAttributes ArrayData::attributes(uint i) const
return static_cast<const SimpleArrayData *>(this)->attributes(i);
}
-ArrayData::Index ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs)
+PropertyIndex ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs)
{
uint idx = mappedIndex(index);
if (idx == UINT_MAX) {
*attrs = Attr_Invalid;
- return { nullptr, 0 };
+ return { nullptr, nullptr };
}
*attrs = attributes(index);
- return { this, attrs->isAccessor() ? idx + 1 /* QV4::Object::SetterOffset*/ : idx };
+ if (attrs->isAccessor())
+ ++idx;
+ return { this, values.values + idx };
}
diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp
new file mode 100644
index 0000000000..650f58463e
--- /dev/null
+++ b/src/qml/jsruntime/qv4arrayiterator.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4arrayiterator_p.h>
+#include <private/qv4typedarray_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(ArrayIteratorObject);
+
+void ArrayIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Array Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const ArrayIteratorObject *thisObject = that->as<ArrayIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not an Array Iterator instance"));
+
+ ScopedObject a(scope, thisObject->d()->iteratedObject);
+ if (!a) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ quint32 index = thisObject->d()->nextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ Scoped<TypedArray> ta(scope, a->as<TypedArray>());
+ quint32 len = a->getLength();
+
+ if (index >= len) {
+ thisObject->d()->iteratedObject.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ thisObject->d()->nextIndex = index + 1;
+ if (itemKind == KeyIteratorKind) {
+ return IteratorPrototype::createIterResultObject(scope.engine, Primitive::fromInt32(index), false);
+ }
+
+ ReturnedValue elementValue = a->get(index);
+ CHECK_EXCEPTION();
+
+ if (itemKind == ValueIteratorKind) {
+ return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false);
+ } else {
+ Q_ASSERT(itemKind == KeyValueIteratorKind);
+
+ ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, Primitive::fromInt32(index));
+ resultArray->arrayPut(1, Value::fromReturnedValue(elementValue));
+ resultArray->setArrayLengthUnchecked(2);
+
+ return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
+ }
+}
+
diff --git a/src/qml/jsruntime/qv4arrayiterator_p.h b/src/qml/jsruntime/qv4arrayiterator_p.h
new file mode 100644
index 0000000000..6d6bb466f1
--- /dev/null
+++ b/src/qml/jsruntime/qv4arrayiterator_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ARRAYITERATOR_P_H
+#define QV4ARRAYITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4iterator_p.h"
+#include "qv4arraydata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+#define ArrayIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedObject) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, nextIndex)
+
+DECLARE_HEAP_OBJECT(ArrayIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(ArrayIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedObject.set(engine, obj);
+ this->nextIndex = 0;
+ }
+};
+
+}
+
+struct ArrayIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ArrayIteratorObject : Object
+{
+ V4_OBJECT2(ArrayIteratorObject, Object)
+ Q_MANAGED_TYPE(ArrayIteratorObject)
+ V4_PROTOTYPE(arrayIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index bd019d3bcb..05f6b7dfec 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -38,12 +39,15 @@
****************************************************************************/
#include "qv4arrayobject_p.h"
+#include "qv4objectiterator_p.h"
+#include "qv4arrayiterator_p.h"
#include "qv4sparsearray_p.h"
#include "qv4objectproto_p.h"
#include "qv4jscall_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4runtime_p.h"
#include "qv4string_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/qscopedvaluerollback.h>
using namespace QV4;
@@ -55,7 +59,7 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Array"));
}
-ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine();
Scope scope(v4);
@@ -80,22 +84,28 @@ ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value
return a.asReturnedValue();
}
-ReturnedValue ArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue ArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineDefaultProperty(QStringLiteral("isArray"), method_isArray, 1);
+ ctor->defineDefaultProperty(QStringLiteral("of"), method_of, 0);
+ ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1);
+ ctor->addSymbolSpecies();
+
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
+ defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2);
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
defineDefaultProperty(QStringLiteral("find"), method_find, 1);
defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1);
defineDefaultProperty(QStringLiteral("join"), method_join, 1);
@@ -107,15 +117,23 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
defineDefaultProperty(QStringLiteral("splice"), method_splice, 2);
defineDefaultProperty(QStringLiteral("unshift"), method_unshift, 1);
+ defineDefaultProperty(QStringLiteral("includes"), method_includes, 1);
defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
defineDefaultProperty(QStringLiteral("every"), method_every, 1);
+ defineDefaultProperty(QStringLiteral("fill"), method_fill, 1);
defineDefaultProperty(QStringLiteral("some"), method_some, 1);
defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
defineDefaultProperty(QStringLiteral("map"), method_map, 1);
defineDefaultProperty(QStringLiteral("filter"), method_filter, 1);
defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1);
defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
+ ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
+ ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
+ engine->jsObjects[ExecutionEngine::ArrayProtoValues] = values;
+ defineDefaultProperty(QStringLiteral("values"), values);
+ defineDefaultProperty(engine->symbol_iterator(), values);
}
ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -124,6 +142,200 @@ ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value
return Encode(isArray);
}
+ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len)
+{
+ ScopedObject a(scope, Primitive::undefinedValue());
+
+ if (ctor) {
+ // ### the spec says that we should only call constructors if
+ // IsConstructor(that), but we have no way of knowing if a builtin is a
+ // constructor. so for the time being, just try call it, and silence any
+ // exceptions-- this is not ideal, as the spec also says that we should
+ // return on exception.
+ //
+ // this also isn't completely kosher. for instance:
+ // Array.from.call(Object, []).constructor == Object
+ // is expected by the tests, but naturally, we get Number.
+ ScopedValue argument(scope, useLen ? QV4::Encode(len) : Primitive::undefinedValue());
+ a = ctor->callAsConstructor(argument, useLen ? 1 : 0);
+ if (scope.engine->hasException)
+ scope.engine->catchException(); // probably not a constructor, then.
+ }
+
+ if (!a) {
+ a = scope.engine->newArrayObject(len);
+ }
+
+ return a;
+}
+
+ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(builtin);
+ ScopedFunctionObject thatCtor(scope, thisObject);
+ ScopedObject itemsObject(scope, argv[0]);
+ bool usingIterator = false;
+
+ if (itemsObject) {
+ // If the object claims to support iterators, then let's try use them.
+ ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator()));
+ if (!it->isNullOrUndefined()) {
+ ScopedFunctionObject itfunc(scope, it);
+ if (!itfunc)
+ return scope.engine->throwTypeError();
+ usingIterator = true;
+ }
+ }
+
+ ScopedFunctionObject mapfn(scope, Primitive::undefinedValue());
+ Value *mapArguments = nullptr;
+ if (argc > 1) {
+ mapfn = ScopedFunctionObject(scope, argv[1]);
+ if (!mapfn)
+ return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow()));
+ mapArguments = scope.alloc(2);
+ }
+
+ ScopedValue thisArg(scope);
+ if (argc > 2)
+ thisArg = argv[2];
+
+ if (usingIterator) {
+ // Item iteration supported, so let's go ahead and try use that.
+ ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0));
+ CHECK_EXCEPTION();
+ ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true));
+ CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
+ if (!iterator) {
+ return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
+ }
+
+ qint64 k = 0;
+ ScopedValue mappedValue(scope);
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+
+ // The loop below pulls out all the properties using the iterator, and
+ // sets them into the created array.
+ forever {
+ if (k > (static_cast<qint64>(1) << 53) - 1) {
+ ScopedValue falsey(scope, Encode(false));
+ ScopedValue error(scope, scope.engine->throwTypeError());
+ return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ }
+
+ // Retrieve the next value. If the iteration ends, we're done here.
+ done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue));
+ CHECK_EXCEPTION();
+ if (done->toBoolean()) {
+ if (ArrayObject *ao = a->as<ArrayObject>()) {
+ ao->setArrayLengthUnchecked(k);
+ } else {
+ a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+ return a.asReturnedValue();
+ }
+
+ if (mapfn) {
+ mapArguments[0] = *nextValue;
+ mapArguments[1] = Primitive::fromDouble(k);
+ mappedValue = mapfn->call(thisArg, mapArguments, 2);
+ if (scope.engine->hasException)
+ return Runtime::method_iteratorClose(scope.engine, iterator, Primitive::fromBoolean(false));
+ } else {
+ mappedValue = *nextValue;
+ }
+
+ if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) == Attr_Invalid) {
+ a->arraySet(k, mappedValue);
+ } else {
+ // Don't return: we need to close the iterator.
+ scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+ }
+
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ }
+
+ k++;
+ }
+
+ // the return is hidden up in the loop above, when iteration finishes.
+ } else {
+ // Array-like fallback. We request properties by index, and set them on
+ // the return object.
+ ScopedObject arrayLike(scope, argv[0].toObject(scope.engine));
+ if (!arrayLike)
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow()));
+ qint64 len = arrayLike->getLength();
+ ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, true, len));
+ CHECK_EXCEPTION();
+
+ qint64 k = 0;
+ ScopedValue mappedValue(scope, Primitive::undefinedValue());
+ ScopedValue kValue(scope);
+ while (k < len) {
+ kValue = arrayLike->get(k);
+ CHECK_EXCEPTION();
+
+ if (mapfn) {
+ mapArguments[0] = kValue;
+ mapArguments[1] = Primitive::fromDouble(k);
+ mappedValue = mapfn->call(thisArg, mapArguments, 2);
+ CHECK_EXCEPTION();
+ } else {
+ mappedValue = kValue;
+ }
+
+ if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid)
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+
+ a->arraySet(k, mappedValue);
+ CHECK_EXCEPTION();
+
+ k++;
+ }
+
+ if (ArrayObject *ao = a->as<ArrayObject>()) {
+ ao->setArrayLengthUnchecked(k);
+ } else {
+ a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+ return a.asReturnedValue();
+ }
+
+}
+
+ReturnedValue ArrayPrototype::method_of(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(builtin);
+ ScopedFunctionObject that(scope, thisObject);
+ ScopedObject a(createObjectFromCtorOrArray(scope, that, true, argc));
+ CHECK_EXCEPTION();
+
+ int k = 0;
+ while (k < argc) {
+ if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid) {
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+ }
+ a->arraySet(k, argv[k]);
+ CHECK_EXCEPTION();
+
+ k++;
+ }
+
+ // ArrayObject updates its own length, and will throw if we try touch it.
+ if (!a->as<ArrayObject>()) {
+ a->set(scope.engine->id_length(), Primitive::fromDouble(argc), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+
+ return a.asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(builtin);
@@ -171,9 +383,9 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value
} else if (eltAsObj && eltAsObj->isListType()) {
const uint startIndex = result->getLength();
for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) {
- entry = eltAsObj->getIndexed(i);
+ entry = eltAsObj->get(i);
// spec says not to throw if this fails
- result->putIndexed(startIndex + i, entry);
+ result->put(startIndex + i, entry);
}
} else {
result->arraySet(result->getLength(), argv[i]);
@@ -183,6 +395,88 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value
return result.asReturnedValue();
}
+ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ double len = instance->getLength();
+ double target = argv[0].toInteger();
+ double start = argv[1].toInteger();
+ double end = len;
+
+ if (argc > 2 && !argv[2].isUndefined()) {
+ end = argv[2].toInteger();
+ }
+
+ double relativeTarget = target;
+ double relativeStart = start;
+ double relativeEnd = end;
+ double from = 0;
+ double to = 0;
+
+ if (relativeTarget < 0) {
+ to = std::max(len+relativeTarget, 0.0);
+ } else {
+ to = std::min(relativeTarget, len);
+ }
+ if (relativeStart < 0) {
+ from = std::max(len+relativeStart, 0.0);
+ } else {
+ from = std::min(relativeStart, len);
+ }
+
+ double fin = 0;
+ if (relativeEnd < 0) {
+ fin = std::max(len+relativeEnd, 0.0);
+ } else {
+ fin = std::min(relativeEnd, len);
+ }
+ double count = std::min(fin-from, len-to);
+ double direction = 1;
+ if (from < to && to < from+count) {
+ direction = -1;
+ from = from + count - 1;
+ to = to + count - 1;
+ }
+
+ while (count > 0) {
+ bool fromPresent = false;
+ ScopedValue fromVal(scope, instance->get(from, &fromPresent));
+
+ if (fromPresent) {
+ instance->setIndexed(to, fromVal, QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ } else {
+ bool didDelete = instance->deleteProperty(PropertyKey::fromArrayIndex(to));
+ CHECK_EXCEPTION();
+ if (!didDelete) {
+ return scope.engine->throwTypeError();
+ }
+ }
+
+ from = from + direction;
+ to = to + direction;
+ count = count - 1;
+ }
+
+ return instance.asReturnedValue();
+}
+
+ReturnedValue ArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -202,7 +496,7 @@ ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *
ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
for (uint k = 0; k < len; ++k) {
- arguments[0] = instance->getIndexed(k);
+ arguments[0] = instance->get(k);
CHECK_EXCEPTION();
arguments[1] = Primitive::fromDouble(k);
@@ -236,7 +530,7 @@ ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Va
ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
for (uint k = 0; k < len; ++k) {
- arguments[0] = instance->getIndexed(k);
+ arguments[0] = instance->get(k);
CHECK_EXCEPTION();
arguments[1] = Primitive::fromDouble(k);
@@ -282,7 +576,7 @@ ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *
if (i)
R += r4;
- e = a->getIndexed(i);
+ e = a->get(i);
CHECK_EXCEPTION();
if (!e->isNullOrUndefined())
R += e->toQString();
@@ -327,10 +621,10 @@ ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *t
RETURN_UNDEFINED();
}
- ScopedValue result(scope, instance->getIndexed(len - 1));
+ ScopedValue result(scope, instance->get(len - 1));
CHECK_EXCEPTION();
- if (!instance->deleteIndexedProperty(len - 1))
+ if (!instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1)))
return scope.engine->throwTypeError();
if (instance->isArrayObject())
@@ -352,9 +646,9 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
instance->arrayCreate();
Q_ASSERT(instance->arrayData());
- uint len = instance->getLength();
+ qint64 len = instance->getLength();
- if (len + argc < len) {
+ if (len + quint64(argc) >= UINT_MAX) {
// ughh... this goes beyond UINT_MAX
double l = len;
ScopedString s(scope);
@@ -381,7 +675,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
len = instance->arrayData()->length();
} else {
for (int i = 0, ei = argc; i < ei; ++i) {
- if (!instance->putIndexed(len + i, argv[i]))
+ if (!instance->put(len + i, argv[i]))
return scope.engine->throwTypeError();
}
len += argc;
@@ -393,7 +687,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
return scope.engine->throwTypeError();
}
- return Encode(len);
+ return Encode(uint(len));
}
ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -403,7 +697,10 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu
if (!instance)
RETURN_UNDEFINED();
- uint length = instance->getLength();
+ qint64 length = instance->getLength();
+ // ### FIXME
+ if (length >= UINT_MAX)
+ return scope.engine->throwRangeError(QLatin1String("Array.prototype.reverse: Length out of range."));
int lo = 0, hi = length - 1;
@@ -411,19 +708,19 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu
ScopedValue hval(scope);
for (; lo < hi; ++lo, --hi) {
bool loExists, hiExists;
- lval = instance->getIndexed(lo, &loExists);
- hval = instance->getIndexed(hi, &hiExists);
+ lval = instance->get(lo, &loExists);
+ hval = instance->get(hi, &hiExists);
CHECK_EXCEPTION();
bool ok;
if (hiExists)
- ok = instance->putIndexed(lo, hval);
+ ok = instance->put(lo, hval);
else
- ok = instance->deleteIndexedProperty(lo);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(lo));
if (ok) {
if (loExists)
- ok = instance->putIndexed(hi, lval);
+ ok = instance->put(hi, lval);
else
- ok = instance->deleteIndexedProperty(hi);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(hi));
}
if (!ok)
return scope.engine->throwTypeError();
@@ -454,23 +751,23 @@ ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value
if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) {
result = instance->arrayData()->vtable()->pop_front(instance);
} else {
- result = instance->getIndexed(0);
+ result = instance->get(uint(0));
CHECK_EXCEPTION();
ScopedValue v(scope);
// do it the slow way
for (uint k = 1; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ v = instance->get(k, &exists);
CHECK_EXCEPTION();
bool ok;
if (exists)
- ok = instance->putIndexed(k - 1, v);
+ ok = instance->put(k - 1, v);
else
- ok = instance->deleteIndexedProperty(k - 1);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1));
if (!ok)
return scope.engine->throwTypeError();
}
- bool ok = instance->deleteIndexedProperty(len - 1);
+ bool ok = instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1));
if (!ok)
return scope.engine->throwTypeError();
}
@@ -518,7 +815,7 @@ ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value
uint n = 0;
for (uint i = start; i < end; ++i) {
bool exists;
- v = o->getIndexed(i, &exists);
+ v = o->get(i, &exists);
CHECK_EXCEPTION();
if (exists)
result->arraySet(n, v);
@@ -548,60 +845,71 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value
if (!instance)
RETURN_UNDEFINED();
- uint len = instance->getLength();
-
- ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
+ qint64 len = instance->getLength();
double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
- uint start;
+ qint64 start;
if (rs < 0)
- start = (uint) qMax(0., len + rs);
+ start = static_cast<qint64>(qMax(0., len + rs));
else
- start = (uint) qMin(rs, (double)len);
+ start = static_cast<qint64>(qMin(rs, static_cast<double>(len)));
+
+ qint64 deleteCount = 0;
+ qint64 itemCount = 0;
+ if (argc == 1) {
+ deleteCount = len - start;
+ } else if (argc > 1){
+ itemCount = argc - 2;
+ double dc = argv[1].toInteger();
+ deleteCount = static_cast<qint64>(qMin(qMax(dc, 0.), double(len - start)));
+ }
- uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start));
+ if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
+ return scope.engine->throwTypeError();
+ if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
+ return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
+ ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
newArray->arrayReserve(deleteCount);
ScopedValue v(scope);
for (uint i = 0; i < deleteCount; ++i) {
bool exists;
- v = instance->getIndexed(start + i, &exists);
+ v = instance->get(start + i, &exists);
CHECK_EXCEPTION();
if (exists)
newArray->arrayPut(i, v);
}
newArray->setArrayLengthUnchecked(deleteCount);
- uint itemCount = argc < 2 ? 0 : argc - 2;
if (itemCount < deleteCount) {
for (uint k = start; k < len - deleteCount; ++k) {
bool exists;
- v = instance->getIndexed(k + deleteCount, &exists);
+ v = instance->get(k + deleteCount, &exists);
CHECK_EXCEPTION();
bool ok;
if (exists)
- ok = instance->putIndexed(k + itemCount, v);
+ ok = instance->put(k + itemCount, v);
else
- ok = instance->deleteIndexedProperty(k + itemCount);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount));
if (!ok)
return scope.engine->throwTypeError();
}
for (uint k = len; k > len - deleteCount + itemCount; --k) {
- if (!instance->deleteIndexedProperty(k - 1))
+ if (!instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1)))
return scope.engine->throwTypeError();
}
} else if (itemCount > deleteCount) {
uint k = len - deleteCount;
while (k > start) {
bool exists;
- v = instance->getIndexed(k + deleteCount - 1, &exists);
+ v = instance->get(k + deleteCount - 1, &exists);
CHECK_EXCEPTION();
bool ok;
if (exists)
- ok = instance->putIndexed(k + itemCount - 1, v);
+ ok = instance->put(k + itemCount - 1, v);
else
- ok = instance->deleteIndexedProperty(k + itemCount - 1);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount - 1));
if (!ok)
return scope.engine->throwTypeError();
--k;
@@ -609,7 +917,7 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value
}
for (uint i = 0; i < itemCount; ++i)
- instance->putIndexed(start + i, argv[i + 2]);
+ instance->put(start + i, argv[i + 2]);
if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))))
return scope.engine->throwTypeError();
@@ -636,17 +944,17 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu
ScopedValue v(scope);
for (uint k = len; k > 0; --k) {
bool exists;
- v = instance->getIndexed(k - 1, &exists);
+ v = instance->get(k - 1, &exists);
bool ok;
if (exists)
- ok = instance->putIndexed(k + argc - 1, v);
+ ok = instance->put(k + argc - 1, v);
else
- ok = instance->deleteIndexedProperty(k + argc - 1);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + argc - 1));
if (!ok)
return scope.engine->throwTypeError();
}
for (int i = 0, ei = argc; i < ei; ++i) {
- bool ok = instance->putIndexed(i, argv[i]);
+ bool ok = instance->put(i, argv[i]);
if (!ok)
return scope.engine->throwTypeError();
}
@@ -663,6 +971,44 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu
return Encode(newLen);
}
+ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ qint64 len = instance->getLength();
+ if (len == 0) {
+ return Encode(false);
+ }
+
+ double n = 0;
+ if (argc > 1 && !argv[1].isUndefined()) {
+ n = argv[1].toInteger();
+ }
+
+ double k = 0;
+ if (n >= 0) {
+ k = n;
+ } else {
+ k = len + n;
+ if (k < 0) {
+ k = 0;
+ }
+ }
+
+ while (k < len) {
+ ScopedValue val(scope, instance->get(k));
+ if (val->sameValueZero(argv[0])) {
+ return Encode(true);
+ }
+ k++;
+ }
+
+ return Encode(false);
+}
+
ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -691,7 +1037,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
ScopedValue v(scope);
for (uint k = fromIndex; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ v = instance->get(k, &exists);
if (exists && RuntimeHelpers::strictEqual(v, searchValue))
return Encode(k);
}
@@ -705,7 +1051,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
// lets be safe and slow
for (uint i = fromIndex; i < len; ++i) {
bool exists;
- value = instance->getIndexed(i, &exists);
+ value = instance->get(i, &exists);
CHECK_EXCEPTION();
if (exists && RuntimeHelpers::strictEqual(value, searchValue))
return Encode(i);
@@ -729,6 +1075,18 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
return Encode(-1);
}
+ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -765,7 +1123,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const
for (uint k = fromIndex; k > 0;) {
--k;
bool exists;
- v = instance->getIndexed(k, &exists);
+ v = instance->get(k, &exists);
CHECK_EXCEPTION();
if (exists && RuntimeHelpers::strictEqual(v, searchValue))
return Encode(k);
@@ -793,7 +1151,7 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value
bool ok = true;
for (uint k = 0; ok && k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
@@ -805,6 +1163,42 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value
return Encode(ok);
}
+ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ uint len = instance->getLength();
+ int relativeStart = argc > 1 ? argv[1].toInteger() : 0;
+ int relativeEnd = len;
+ if (argc > 2 && !argv[2].isUndefined()) {
+ relativeEnd = argv[2].toInteger();
+ }
+ uint k = 0;
+ uint fin = 0;
+
+ if (relativeStart < 0) {
+ k = std::max(len+relativeStart, uint(0));
+ } else {
+ k = std::min(uint(relativeStart), len);
+ }
+
+ if (relativeEnd < 0) {
+ fin = std::max(len + relativeEnd, uint(0));
+ } else {
+ fin = std::min(uint(relativeEnd), len);
+ }
+
+ while (k < fin) {
+ instance->setIndexed(k, argv[0], QV4::Object::DoThrowOnRejection);
+ k++;
+ }
+
+ return instance.asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -824,7 +1218,7 @@ ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
@@ -855,7 +1249,7 @@ ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Valu
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
@@ -873,12 +1267,15 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t
if (!instance)
RETURN_UNDEFINED();
- uint len = instance->getLength();
+ qint64 len = instance->getLength();
if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+ if (len > UINT_MAX - 1)
+ return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
+
ScopedArrayObject a(scope, scope.engine->newArrayObject());
a->arrayReserve(len);
a->setArrayLengthUnchecked(len);
@@ -890,7 +1287,7 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
@@ -925,7 +1322,7 @@ ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value
uint to = 0;
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
@@ -962,7 +1359,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value
} else {
bool kPresent = false;
while (k < len && !kPresent) {
- v = instance->getIndexed(k, &kPresent);
+ v = instance->get(k, &kPresent);
if (kPresent)
acc = v;
++k;
@@ -975,7 +1372,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value
while (k < len) {
bool kPresent;
- v = instance->getIndexed(k, &kPresent);
+ v = instance->get(k, &kPresent);
if (kPresent) {
arguments[0] = acc;
arguments[1] = v;
@@ -1015,7 +1412,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const
} else {
bool kPresent = false;
while (k > 0 && !kPresent) {
- v = instance->getIndexed(k - 1, &kPresent);
+ v = instance->get(k - 1, &kPresent);
if (kPresent)
acc = v;
--k;
@@ -1028,7 +1425,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const
while (k > 0) {
bool kPresent;
- v = instance->getIndexed(k - 1, &kPresent);
+ v = instance->get(k - 1, &kPresent);
if (kPresent) {
arguments[0] = acc;
arguments[1] = v;
@@ -1041,3 +1438,20 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const
return acc->asReturnedValue();
}
+ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ return thisObject->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h
index 3825a600a2..04ec7e1607 100644
--- a/src/qml/jsruntime/qv4arrayobject_p.h
+++ b/src/qml/jsruntime/qv4arrayobject_p.h
@@ -70,8 +70,8 @@ struct ArrayCtor: FunctionObject
{
V4_OBJECT2(ArrayCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct ArrayPrototype: ArrayObject
@@ -79,9 +79,13 @@ struct ArrayPrototype: ArrayObject
void init(ExecutionEngine *engine, Object *ctor);
static ReturnedValue method_isArray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_from(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_copyWithin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -93,15 +97,23 @@ struct ArrayPrototype: ArrayObject
static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_unshift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fill(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ // while this function is implemented here, it's the same for many other JS classes, so the corresponding JS function
+ // is instantiated in the engine, and it can be added to any JS object through Object::addSymbolSpecies()
+ static ReturnedValue method_get_species(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp
index eb83f902db..f00abad871 100644
--- a/src/qml/jsruntime/qv4booleanobject.cpp
+++ b/src/qml/jsruntime/qv4booleanobject.cpp
@@ -50,13 +50,13 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Boolean"));
}
-ReturnedValue BooleanCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc)
+ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *)
{
bool n = argc ? argv[0].toBoolean() : false;
return Encode(that->engine()->newBooleanObject(n));
}
-ReturnedValue BooleanCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue BooleanCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc)
{
bool value = argc ? argv[0].toBoolean() : 0;
return Encode(value);
@@ -66,7 +66,7 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString);
diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h
index 3cf09b2667..276ec8393b 100644
--- a/src/qml/jsruntime/qv4booleanobject_p.h
+++ b/src/qml/jsruntime/qv4booleanobject_p.h
@@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject
{
V4_OBJECT2(BooleanCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct BooleanPrototype: BooleanObject
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 020e519e74..bb08d2786d 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -48,12 +48,48 @@
#include "qv4errorobject_p.h"
#include "qv4string_p.h"
#include "qv4qmlcontext_p.h"
+#include "qv4stackframe_p.h"
using namespace QV4;
DEFINE_MANAGED_VTABLE(ExecutionContext);
DEFINE_MANAGED_VTABLE(CallContext);
-DEFINE_MANAGED_VTABLE(CatchContext);
+
+Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex)
+{
+ Function *function = frame->v4Function;
+
+ Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex);
+ uint nLocals = ic->size;
+ size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
+
+ ExecutionEngine *v4 = function->internalClass->engine;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic);
+ c->init();
+ c->type = Heap::ExecutionContext::Type_BlockContext;
+
+ Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
+ c->outer.set(v4, outer);
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
+
+ c->locals.size = nLocals;
+ c->locals.alloc = nLocals;
+
+ return c;
+}
+
+Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context)
+{
+ uint nLocals = context->locals.alloc;
+ size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
+
+ ExecutionEngine *v4 = context->internalClass->engine;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass);
+ memcpy(c, context, requiredMemory);
+
+ return c;
+
+}
Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
{
@@ -75,7 +111,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
uint nLocals = compiledFunction->nLocals;
c->locals.size = nLocals;
c->locals.alloc = localsAndFormals;
- // memory allocated from the JS heap is 0 initialized, so check if undefined is 0
+ // memory allocated from the JS heap is 0 initialized, so check if empty is 0
Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0);
Value *args = c->locals.values + nLocals;
@@ -96,11 +132,14 @@ Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with)
return c;
}
-Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue)
+Heap::ExecutionContext *ExecutionContext::newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName)
{
- Scope scope(this);
- ScopedValue e(scope, exceptionValue);
- return engine()->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e);
+ Scope scope(frame->context());
+ ScopedString name(scope, exceptionVarName);
+ ScopedValue val(scope, scope.engine->catchException(nullptr));
+ ScopedContext ctx(scope, newBlockContext(frame, blockIndex));
+ ctx->setProperty(name, val);
+ return ctx->d();
}
void ExecutionContext::createMutableBinding(String *name, bool deletable)
@@ -132,44 +171,32 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
activation = ctx->d()->activation;
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
+ // never create activation records on block contexts
default:
break;
}
ctx = ctx->d()->outer;
}
- if (activation->hasOwnProperty(name))
+ PropertyKey id = name->toPropertyKey();
+ if (activation->getOwnProperty(id) != Attr_Invalid)
return;
ScopedProperty desc(scope);
PropertyAttributes attrs(Attr_Data);
attrs.setConfigurable(deletable);
- activation->__defineOwnProperty__(scope.engine, name, desc, attrs);
-}
-
-void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName,
- const Value &exceptionValue)
-{
- Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext);
- outer.set(internalClass->engine, outerContext);
-
- this->exceptionVarName.set(internalClass->engine, exceptionVarName);
- this->exceptionValue.set(internalClass->engine, exceptionValue);
+ if (!activation->defineOwnProperty(id, desc, attrs))
+ scope.engine->throwTypeError();
}
bool ExecutionContext::deleteProperty(String *name)
{
- name->makeIdentifier();
- Identifier *id = name->identifier();
+ PropertyKey id = name->toPropertyKey();
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return false;
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
uint index = c->internalClass->find(id);
@@ -183,8 +210,8 @@ bool ExecutionContext::deleteProperty(String *name)
if (ctx->activation) {
Scope scope(this);
ScopedObject object(scope, ctx->activation);
- if (object && object->hasProperty(name))
- return object->deleteProperty(name);
+ if (object && object->hasProperty(name->toPropertyKey()))
+ return object->deleteProperty(name->toPropertyKey());
}
break;
}
@@ -199,32 +226,24 @@ bool ExecutionContext::deleteProperty(String *name)
ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value)
{
- name->makeIdentifier();
- Identifier *id = name->identifier();
+ PropertyKey id = name->toPropertyKey();
QV4::ExecutionEngine *v4 = engine();
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d())) {
- c->exceptionValue.set(v4, value);
- return NoError;
- }
- break;
- }
case Heap::ExecutionContext::Type_WithContext: {
Scope scope(v4);
ScopedObject w(scope, ctx->activation);
- if (w->hasProperty(name)) {
+ if (w->hasProperty(name->toPropertyKey())) {
if (!w->put(name, value))
return TypeError;
return NoError;
}
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
uint index = c->internalClass->find(id);
@@ -262,20 +281,14 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value
ReturnedValue ExecutionContext::getProperty(String *name)
{
- name->makeIdentifier();
+ PropertyKey id = name->toPropertyKey();
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return c->exceptionValue.asReturnedValue();
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- Identifier *id = name->identifier();
uint index = c->internalClass->find(id);
if (index < UINT_MAX)
@@ -289,7 +302,7 @@ ReturnedValue ExecutionContext::getProperty(String *name)
Scope scope(this);
ScopedObject activation(scope, ctx->activation);
bool hasProperty = false;
- ReturnedValue v = activation->get(name, &hasProperty);
+ ReturnedValue v = activation->get(id, nullptr, &hasProperty);
if (hasProperty)
return v;
}
@@ -303,21 +316,14 @@ ReturnedValue ExecutionContext::getProperty(String *name)
ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
{
base->setM(nullptr);
- name->makeIdentifier();
+ PropertyKey id = name->toPropertyKey();
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return c->exceptionValue.asReturnedValue();
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- name->makeIdentifier();
- Identifier *id = name->identifier();
uint index = c->internalClass->find(id);
if (index < UINT_MAX)
@@ -340,7 +346,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
Scope scope(this);
ScopedObject o(scope, ctx->activation);
bool hasProperty = false;
- ReturnedValue v = o->get(name, &hasProperty);
+ ReturnedValue v = o->get(id, nullptr, &hasProperty);
if (hasProperty) {
base->setM(o->d());
return v;
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 512bfa06d8..fbb4168e9b 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -55,66 +55,11 @@
QT_BEGIN_NAMESPACE
-class QObject;
-class QQmlContextData;
-
namespace QV4 {
-namespace CompiledData {
-struct CompilationUnitBase;
-struct Function;
-}
-
-struct Function;
-struct Identifier;
-struct CallContext;
-struct CatchContext;
-struct QmlContext;
-struct QQmlContextWrapper;
-
-struct CallData
-{
- enum Offsets {
- Function = 0,
- Context = 1,
- Accumulator = 2,
- This = 3,
- Argc = 4
- };
-
- Value function;
- Value context;
- Value accumulator;
- Value thisObject;
- Value _argc;
-
- int argc() const {
- Q_ASSERT(_argc.isInteger());
- return _argc.int_32();
- }
-
- void setArgc(int argc) {
- Q_ASSERT(argc >= 0);
- _argc.setInt_32(argc);
- }
-
- inline ReturnedValue argument(int i) const {
- return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue();
- }
-
- Value args[1];
-
- static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); }
-};
-
-Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
-Q_STATIC_ASSERT(offsetof(CallData, thisObject) == CallData::This*sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value));
namespace Heap {
-struct QmlContext;
-
#define ExecutionContextMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, outer) \
Member(class, Pointer, Object *, activation)
@@ -124,9 +69,9 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
enum ContextType {
Type_GlobalContext = 0x1,
- Type_CatchContext = 0x2,
- Type_WithContext = 0x3,
- Type_QmlContext = 0x4,
+ Type_WithContext = 0x2,
+ Type_QmlContext = 0x3,
+ Type_BlockContext = 0x4,
Type_CallContext = 0x5
};
@@ -137,6 +82,10 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
type = t;
}
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
quint32 type : 8;
quint32 nArgs : 24;
#if QT_POINTER_SIZE == 8
@@ -181,16 +130,6 @@ Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0);
//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData));
//#endif
-#define CatchContextMembers(class, Member) \
- Member(class, Pointer, String *, exceptionVarName) \
- Member(class, HeapValue, HeapValue, exceptionValue)
-
-DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) {
- DECLARE_MARKOBJECTS(CatchContext);
-
- void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue);
-};
-Q_STATIC_ASSERT(std::is_trivial< CatchContext >::value);
}
@@ -204,9 +143,11 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
Q_MANAGED_TYPE(ExecutionContext)
V4_INTERNALCLASS(ExecutionContext)
+ static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex);
+ static Heap::CallContext *cloneBlockContext(Heap::CallContext *context);
static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame);
Heap::ExecutionContext *newWithContext(Heap::Object *with);
- Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue);
+ static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName);
void createMutableBinding(String *name, bool deletable);
@@ -224,6 +165,12 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
inline CallContext *asCallContext();
inline const CallContext *asCallContext() const;
+
+protected:
+ // vtable method required for compilation
+ static bool virtualDeleteProperty(Managed *, PropertyKey) {
+ Q_UNREACHABLE();
+ }
};
struct Q_QML_EXPORT CallContext : public ExecutionContext
@@ -239,11 +186,6 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext
}
};
-struct CatchContext : public ExecutionContext
-{
- V4_MANAGED(CatchContext, ExecutionContext)
-};
-
inline CallContext *ExecutionContext::asCallContext()
{
return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : nullptr;
diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp
index d894d909ff..d550e559d3 100644
--- a/src/qml/jsruntime/qv4dataview.cpp
+++ b/src/qml/jsruntime/qv4dataview.cpp
@@ -40,6 +40,7 @@
#include "qv4dataview_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/private/qnumeric_p.h>
#include "qendian.h"
@@ -54,7 +55,7 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("DataView"));
}
-ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Scope scope(f->engine());
Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue());
@@ -69,54 +70,57 @@ ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Val
if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength)
return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
- Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>());
+ Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>());
a->d()->buffer.set(scope.engine, buffer->d());
a->d()->byteLength = byteLength;
a->d()->byteOffset = byteOffset;
return a.asReturnedValue();
}
-ReturnedValue DataViewCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
- defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 0);
- defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 0);
- defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 0);
- defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 0);
- defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 0);
-
- defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 0);
- defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 0);
- defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 0);
- defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 0);
- defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 0);
+ defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 1);
+ defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 1);
+ defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 1);
+ defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 1);
+ defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 1);
+ defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 1);
+
+ defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 2);
+ defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 2);
+ defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 2);
+ defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 2);
+ defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 2);
+ defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 2);
+ defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 2);
+ defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 2);
+
+ ScopedString name(scope, engine->newString(QStringLiteral("DataView")));
+ defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
// For backword compatibility
- defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0);
+ defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 1);
}
ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h
index 1e07d85118..6a9f865c0f 100644
--- a/src/qml/jsruntime/qv4dataview_p.h
+++ b/src/qml/jsruntime/qv4dataview_p.h
@@ -79,8 +79,8 @@ struct DataViewCtor: FunctionObject
{
V4_OBJECT2(DataViewCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct DataView : Object
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 5bbe312146..3efd626685 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -44,6 +44,7 @@
#include "qv4runtime_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/QDebug>
#include <QtCore/QDateTime>
@@ -89,9 +90,6 @@ static const double msPerMinute = 60000.0;
static const double msPerHour = 3600000.0;
static const double msPerDay = 86400000.0;
-// The current *standard* time offset, regardless of DST:
-static double LocalTZA = 0.0; // initialized at startup
-
static inline double TimeWithinDay(double t)
{
double r = ::fmod(t, msPerDay);
@@ -318,14 +316,14 @@ static inline double MakeDate(double day, double time)
against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725
*/
-static inline double DaylightSavingTA(double t) // t is a UTC time
+static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
{
return QTimeZone::systemTimeZone().offsetFromUtc(
- QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - LocalTZA;
+ QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA;
}
#else
// This implementation fails to take account of past changes in standard offset.
-static inline double DaylightSavingTA(double t)
+static inline double DaylightSavingTA(double t, double /*localTZA*/)
{
struct tm tmtm;
#if defined(Q_CC_MSVC)
@@ -348,19 +346,19 @@ static inline double DaylightSavingTA(double t)
}
#endif // USE_QTZ_SYSTEM_ZONE
-static inline double LocalTime(double t)
+static inline double LocalTime(double t, double localTZA)
{
// Flawed, yet verbatim from the spec:
- return t + LocalTZA + DaylightSavingTA(t);
+ return t + localTZA + DaylightSavingTA(t, localTZA);
}
// The spec does note [*] that UTC and LocalTime are not quite mutually inverse.
// [*] http://www.ecma-international.org/ecma-262/7.0/index.html#sec-utc-t
-static inline double UTC(double t)
+static inline double UTC(double t, double localTZA)
{
// Flawed, yet verbatim from the spec:
- return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
+ return t - localTZA - DaylightSavingTA(t - localTZA, localTZA);
}
static inline double currentTime()
@@ -377,7 +375,7 @@ static inline double TimeClip(double t)
return Primitive::toInteger(t) + 0;
}
-static inline double ParseString(const QString &s)
+static inline double ParseString(const QString &s, double localTZA)
{
/*
First, try the format defined in ECMA 262's "Date Time String Format";
@@ -533,9 +531,9 @@ static inline double ParseString(const QString &s)
if (seenZ)
t -= offset * offsetSign * 60 * 1000;
else if (seenT) // No zone specified, treat date-time as local time
- t = UTC(t);
+ t = UTC(t, localTZA);
// else: treat plain date as already in UTC
- return t;
+ return TimeClip(t);
}
QDateTime dt = QDateTime::fromString(s, Qt::TextDate);
@@ -605,7 +603,7 @@ static inline double ParseString(const QString &s)
}
if (!dt.isValid())
return qt_qnan();
- return dt.toMSecsSinceEpoch();
+ return TimeClip(dt.toMSecsSinceEpoch());
}
/*!
@@ -621,12 +619,12 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec)
return QDateTime::fromMSecsSinceEpoch(t, Qt::UTC).toTimeSpec(spec);
}
-static inline QString ToString(double t)
+static inline QString ToString(double t, double localTZA)
{
if (std::isnan(t))
return QStringLiteral("Invalid Date");
QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT");
- double tzoffset = LocalTZA + DaylightSavingTA(t);
+ double tzoffset = localTZA + DaylightSavingTA(t, localTZA);
if (tzoffset) {
int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60);
int mins = int(::fabs(tzoffset) / 1000 / 60) % 60;
@@ -705,7 +703,7 @@ DEFINE_OBJECT_VTABLE(DateObject);
void Heap::DateObject::init(const QDateTime &date)
{
Object::init();
- this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan();
+ this->date = date.isValid() ? TimeClip(date.toMSecsSinceEpoch()) : qt_qnan();
}
void Heap::DateObject::init(const QTime &time)
@@ -730,7 +728,7 @@ void Heap::DateObject::init(const QTime &time)
*/
static const double d = MakeDay(1925, 5, 8);
double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec());
- date = TimeClip(UTC(MakeDate(d, t)));
+ date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA));
}
QDateTime DateObject::toQDateTime() const
@@ -745,15 +743,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Date"));
}
-ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc)
+ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *)
{
+ ExecutionEngine *e = that->engine();
double t = 0;
if (argc == 0)
t = currentTime();
else if (argc == 1) {
- Scope scope(that->engine());
+ Scope scope(e);
ScopedValue arg(scope, argv[0]);
if (DateObject *d = arg->as<DateObject>()) {
t = d->date();
@@ -761,7 +760,7 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu
arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT);
if (String *s = arg->stringValue())
- t = ParseString(s->toQString());
+ t = ParseString(s->toQString(), e->localTZA);
else
t = TimeClip(arg->toNumber());
}
@@ -778,16 +777,17 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu
if (year >= 0 && year <= 99)
year += 1900;
t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms));
- t = TimeClip(UTC(t));
+ t = TimeClip(UTC(t, e->localTZA));
}
- return Encode(that->engine()->newDateObject(Primitive::fromDouble(t)));
+ return Encode(e->newDateObject(Primitive::fromDouble(t)));
}
-ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int)
+ReturnedValue DateCtor::virtualCall(const FunctionObject *m, const Value *, const Value *, int)
{
+ ExecutionEngine *e = m->engine();
double t = currentTime();
- return m->engine()->newString(ToString(t))->asReturnedValue();
+ return e->newString(ToString(t, e->localTZA))->asReturnedValue();
}
void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -796,7 +796,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7));
- LocalTZA = getLocalTZA();
+ engine->localTZA = getLocalTZA();
ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1);
ctor->defineDefaultProperty(QStringLiteral("UTC"), method_UTC, 7);
@@ -853,15 +853,14 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
QString toGmtString(QStringLiteral("toGMTString"));
ScopedString us(scope, engine->newIdentifier(toUtcString));
ScopedString gs(scope, engine->newIdentifier(toGmtString));
- ExecutionContext *global = engine->rootContext();
- ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(global, us, method_toUTCString));
- toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(engine, us, method_toUTCString, 0));
defineDefaultProperty(us, toUtcGmtStringFn);
defineDefaultProperty(gs, toUtcGmtStringFn);
}
defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0);
defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1);
+ defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable);
}
double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
@@ -872,12 +871,12 @@ double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
return 0;
}
-ReturnedValue DatePrototype::method_parse(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue DatePrototype::method_parse(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
if (!argc)
return Encode(qt_qnan());
else
- return Encode(ParseString(argv[0].toQString()));
+ return Encode(ParseString(argv[0].toQString(), f->engine()->localTZA));
}
ReturnedValue DatePrototype::method_UTC(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -909,7 +908,7 @@ ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Valu
{
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
- return Encode(v4->newString(ToString(t)));
+ return Encode(v4->newString(ToString(t, v4->localTZA)));
}
ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -966,7 +965,7 @@ ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = YearFromTime(LocalTime(t)) - 1900;
+ t = YearFromTime(LocalTime(t, v4->localTZA)) - 1900;
return Encode(t);
}
@@ -975,7 +974,7 @@ ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const V
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = YearFromTime(LocalTime(t));
+ t = YearFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -993,7 +992,7 @@ ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Valu
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = MonthFromTime(LocalTime(t));
+ t = MonthFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1011,7 +1010,7 @@ ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = DateFromTime(LocalTime(t));
+ t = DateFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1029,7 +1028,7 @@ ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = WeekDay(LocalTime(t));
+ t = WeekDay(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1047,7 +1046,7 @@ ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Valu
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = HourFromTime(LocalTime(t));
+ t = HourFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1065,7 +1064,7 @@ ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Va
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = MinFromTime(LocalTime(t));
+ t = MinFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1083,7 +1082,7 @@ ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Va
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = SecFromTime(LocalTime(t));
+ t = SecFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1101,7 +1100,7 @@ ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, con
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = msFromTime(LocalTime(t));
+ t = msFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1119,7 +1118,7 @@ ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, c
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = (t - LocalTime(t)) / msPerMinute;
+ t = (t - LocalTime(t, v4->localTZA)) / msPerMinute;
return Encode(t);
}
@@ -1144,13 +1143,13 @@ ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, con
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double ms = argc ? argv[0].toNumber() : qt_qnan();
if (v4->hasException)
return QV4::Encode::undefined();
- self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
+ self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA)));
return Encode(self->date());
}
@@ -1178,7 +1177,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double sec = argc ? argv[0].toNumber() : qt_qnan();
@@ -1187,7 +1186,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va
double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1214,7 +1213,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double min = argc ? argv[0].toNumber() : qt_qnan();
@@ -1226,7 +1225,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va
double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1254,7 +1253,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double hour = argc ? argv[0].toNumber() : qt_qnan();
@@ -1269,7 +1268,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu
double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1298,13 +1297,13 @@ ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double date = argc ? argv[0].toNumber() : qt_qnan();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1334,7 +1333,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double month = argc ? argv[0].toNumber() : qt_qnan();
@@ -1343,7 +1342,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu
double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1374,7 +1373,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value
if (std::isnan(t))
t = 0;
else
- t = LocalTime(t);
+ t = LocalTime(t, v4->localTZA);
double year = argc ? argv[0].toNumber() : qt_qnan();
double r;
if (std::isnan(year)) {
@@ -1383,7 +1382,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value
if ((Primitive::toInteger(year) >= 0) && (Primitive::toInteger(year) <= 99))
year += 1900;
r = MakeDay(year, MonthFromTime(t), DateFromTime(t));
- r = UTC(MakeDate(r, TimeWithinDay(t)));
+ r = UTC(MakeDate(r, TimeWithinDay(t)), v4->localTZA);
r = TimeClip(r);
}
self->setDate(r);
@@ -1413,7 +1412,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
if (std::isnan(t))
@@ -1427,7 +1426,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V
double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1471,7 +1470,7 @@ ReturnedValue DatePrototype::method_toISOString(const FunctionObject *b, const V
int year = (int)YearFromTime(t);
if (year < 0 || year > 9999) {
if (qAbs(year) >= 1000000)
- RETURN_RESULT(v4->newString(QStringLiteral("Invalid Date")));
+ RETURN_RESULT(v4->throwRangeError(*thisObject));
result += year < 0 ? QLatin1Char('-') : QLatin1Char('+');
year = qAbs(year);
addZeroPrefixedInt(result, year, 6);
@@ -1518,7 +1517,23 @@ ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value
return toIso->call(O, nullptr, 0);
}
-void DatePrototype::timezoneUpdated()
+ReturnedValue DatePrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!thisObject->isObject() || !argc || !argv->isString())
+ return e->throwTypeError();
+
+ String *hint = argv->stringValue();
+ PropertyKey id = hint->toPropertyKey();
+ if (id == e->id_default()->propertyKey())
+ hint = e->id_string();
+ else if (id != e->id_string()->propertyKey() && id != e->id_number()->propertyKey())
+ return e->throwTypeError();
+
+ return RuntimeHelpers::ordinaryToPrimitive(e, static_cast<const Object *>(thisObject), hint);
+}
+
+void DatePrototype::timezoneUpdated(ExecutionEngine *e)
{
- LocalTZA = getLocalTZA();
+ e->localTZA = getLocalTZA();
}
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index 2b9a580288..5b9934282c 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -101,15 +101,15 @@ struct DateObject: Object {
template<>
inline const DateObject *Value::as() const {
- return isManaged() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr;
}
struct DateCtor: FunctionObject
{
V4_OBJECT2(DateCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int);
};
struct DatePrototype: Object
@@ -169,8 +169,9 @@ struct DatePrototype: Object
static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int);
- static void timezoneUpdated();
+ static void timezoneUpdated(ExecutionEngine *e);
};
}
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 0ed0df89a9..f9ef6b45a1 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -42,6 +42,9 @@
#include <qv4object_p.h>
#include <qv4objectproto_p.h>
#include <qv4objectiterator_p.h>
+#include <qv4setiterator_p.h>
+#include <qv4mapiterator_p.h>
+#include <qv4arrayiterator_p.h>
#include <qv4arrayobject_p.h>
#include <qv4booleanobject_p.h>
#include <qv4globalobject_p.h>
@@ -52,6 +55,9 @@
#include <qv4numberobject_p.h>
#include <qv4regexpobject_p.h>
#include <qv4regexp_p.h>
+#include "qv4symbol_p.h"
+#include "qv4setobject_p.h"
+#include "qv4mapobject_p.h"
#include <qv4variantobject_p.h>
#include <qv4runtime_p.h>
#include <private/qv4mm_p.h>
@@ -63,7 +69,17 @@
#include "qv4debugging_p.h"
#include "qv4profiling_p.h"
#include "qv4executableallocator_p.h"
+#include "qv4iterator_p.h"
+#include "qv4stringiterator_p.h"
+#include "qv4generatorobject_p.h"
+#include "qv4reflect_p.h"
+#include "qv4proxy_p.h"
+#include "qv4stackframe_p.h"
+
+#if QT_CONFIG(qml_sequence_object)
#include "qv4sequenceobject_p.h"
+#endif
+
#include "qv4qobjectwrapper_p.h"
#include "qv4memberdata_p.h"
#include "qv4arraybuffer_p.h"
@@ -76,7 +92,9 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmllist_p.h>
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <QtCore/QTextStream>
#include <QDateTime>
@@ -110,7 +128,7 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const
#ifdef V4_BOOTSTRAP
QJSEngine *ExecutionEngine::jsEngine() const
{
- return v8Engine->publicEngine();
+ return publicEngine;
}
QQmlEngine *ExecutionEngine::qmlEngine() const
@@ -121,7 +139,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const
qint32 ExecutionEngine::maxCallDepth = -1;
-ExecutionEngine::ExecutionEngine()
+ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
, bumperPointerAllocator(new WTF::BumpPointerAllocator)
@@ -129,6 +147,7 @@ ExecutionEngine::ExecutionEngine()
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
, v8Engine(nullptr)
+ , publicEngine(jsEngine)
, argumentsAccessors(nullptr)
, nArgumentsAccessors(0)
, m_engineId(engineSerial.fetchAndAddOrdered(1))
@@ -181,27 +200,44 @@ ExecutionEngine::ExecutionEngine()
}
exceptionValue = jsAlloca(1);
+ *exceptionValue = Encode::undefined();
globalObject = static_cast<Object *>(jsAlloca(1));
jsObjects = jsAlloca(NJSObjects);
typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes));
typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes));
jsStrings = jsAlloca(NJSStrings);
+ jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value);
identifierTable = new IdentifierTable(this);
- classPool = new InternalClassPool;
+ memset(classes, 0, sizeof(classes));
+ classes[Class_Empty] = memoryManager->allocIC<InternalClass>();
+ classes[Class_Empty]->init(this);
- internalClasses[Class_Empty] = new (classPool) InternalClass(this);
- internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable());
- internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
- internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
- internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
- internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
- internalClasses[Class_QmlContext] = internalClasses[EngineBase::Class_ExecutionContext]->changeVTable(QV4::QmlContext::staticVTable());
- internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
+ classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
+ classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
+ classes[Class_SparseArrayData] = classes[Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
+ classes[Class_ExecutionContext] = classes[Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
+ classes[Class_CallContext] = classes[Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
+ classes[Class_QmlContext] = classes[Class_Empty]->changeVTable(QV4::QmlContext::staticVTable());
+
+ Scope scope(this);
+ Scoped<InternalClass> ic(scope);
+ ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable());
+ jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d());
+ classes[Class_Object] = ic->changePrototype(objectPrototype()->d());
+ classes[Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
+
+ ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
+ jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d(), /*init =*/ false);
+ classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable())->changePrototype(stringPrototype()->d());
+ Q_ASSERT(stringPrototype()->d() && classes[Class_String]->prototype);
+
+ jsObjects[SymbolProto] = memoryManager->allocate<SymbolPrototype>();
+ classes[Class_Symbol] = classes[EngineBase::Class_Empty]->changeVTable(QV4::Symbol::staticVTable())->changePrototype(symbolPrototype()->d());
jsStrings[String_Empty] = newIdentifier(QString());
jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined"));
@@ -211,6 +247,8 @@ ExecutionEngine::ExecutionEngine()
jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean"));
jsStrings[String_number] = newIdentifier(QStringLiteral("number"));
jsStrings[String_string] = newIdentifier(QStringLiteral("string"));
+ jsStrings[String_default] = newIdentifier(QStringLiteral("default"));
+ jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol"));
jsStrings[String_object] = newIdentifier(QStringLiteral("object"));
jsStrings[String_function] = newIdentifier(QStringLiteral("function"));
jsStrings[String_length] = newIdentifier(QStringLiteral("length"));
@@ -239,141 +277,186 @@ ExecutionEngine::ExecutionEngine()
jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset"));
jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer"));
jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex"));
-
- InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable());
- jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic);
- internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d());
- internalClasses[EngineBase::Class_QmlContextWrapper] = internalClasses[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
+ jsStrings[String_next] = newIdentifier(QStringLiteral("next"));
+ jsStrings[String_done] = newIdentifier(QStringLiteral("done"));
+ jsStrings[String_return] = newIdentifier(QStringLiteral("return"));
+
+ jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance"));
+ jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable"));
+ jsSymbols[Symbol_iterator] = Symbol::create(this, QStringLiteral("@Symbol.iterator"));
+ jsSymbols[Symbol_match] = Symbol::create(this, QStringLiteral("@Symbol.match"));
+ jsSymbols[Symbol_replace] = Symbol::create(this, QStringLiteral("@Symbol.replace"));
+ jsSymbols[Symbol_search] = Symbol::create(this, QStringLiteral("@Symbol.search"));
+ jsSymbols[Symbol_species] = Symbol::create(this, QStringLiteral("@Symbol.species"));
+ jsSymbols[Symbol_split] = Symbol::create(this, QStringLiteral("@Symbol.split"));
+ jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive"));
+ jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag"));
+ jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables"));
+ jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy"));
ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype());
- Q_ASSERT(ic->prototype);
- ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable);
- Q_ASSERT(ic->prototype);
- jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic, objectPrototype());
- internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
- jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>();
-
- InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
- argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
- internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable);
+ Q_ASSERT(ic->d()->prototype);
+ ic = ic->addMember(id_length()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable);
+ Q_ASSERT(ic->d()->prototype);
+ jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d());
+ classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
+ jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>();
+
+ Scoped<InternalClass> argsClass(scope);
+ argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
+ argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
+ argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
+ classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Data|Attr_NotEnumerable);
argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype());
- argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
- argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
+ argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
+ classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
*static_cast<Value *>(globalObject) = newObject();
Q_ASSERT(globalObject->d()->vtable());
initRootContext();
ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_length(), Attr_ReadOnly);
- jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic);
- internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
- Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex);
+ ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly);
+ classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
+ Q_ASSERT(classes[Class_StringObject]->find(id_length()->propertyKey()) == Heap::StringObject::LengthPropertyIndex);
- jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>();
- jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>();
- jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>();
+ classes[Class_SymbolObject] = newInternalClass(QV4::SymbolObject::staticVTable(), symbolPrototype());
+
+ jsObjects[NumberProto] = memoryManager->allocate<NumberPrototype>();
+ jsObjects[BooleanProto] = memoryManager->allocate<BooleanPrototype>();
+ jsObjects[DateProto] = memoryManager->allocate<DatePrototype>();
uint index;
ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype());
- ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index);
+ ic = ic->addMember(id_prototype()->propertyKey(), Attr_NotEnumerable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic, objectPrototype());
+ jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d());
ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype());
- ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
+ ic = ic->addMember(id_prototype()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- internalClasses[EngineBase::Class_FunctionObject] = ic;
- ic = ic->addMember(id_name(), Attr_ReadOnly, &index);
+ classes[Class_FunctionObject] = ic->d();
+ ic = ic->addMember(id_name()->propertyKey(), Attr_ReadOnly, &index);
Q_ASSERT(index == Heap::ScriptFunction::Index_Name);
ic = ic->changeVTable(ScriptFunction::staticVTable());
- internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index);
+ ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly_ButConfigurable, &index);
Q_ASSERT(index == Heap::ScriptFunction::Index_Length);
- internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index);
+ classes[Class_ScriptFunction] = ic->d();
+ ic = ic->changeVTable(ConstructorFunction::staticVTable());
+ classes[Class_ConstructorFunction] = ic->d();
+ ic = ic->changeVTable(MemberFunction::staticVTable());
+ classes[Class_MemberFunction] = ic->d();
+ ic = ic->changeVTable(GeneratorFunction::staticVTable());
+ classes[Class_MemberFunction] = ic->d();
+ ic = ic->changeVTable(GeneratorFunction::staticVTable());
+ classes[Class_GeneratorFunction] = ic->d();
+ ic = ic->changeVTable(MemberGeneratorFunction::staticVTable());
+ classes[Class_MemberGeneratorFunction] = ic->d();
+ classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->propertyKey(), Attr_NotEnumerable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor);
- Scope scope(this);
+ jsObjects[GeneratorProto] = memoryManager->allocObject<GeneratorPrototype>(classes[Class_Object]);
+ classes[Class_GeneratorObject] = newInternalClass(QV4::GeneratorObject::staticVTable(), generatorPrototype());
+
ScopedString str(scope);
- internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
+ classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
+ ic = ic->addMember(id_lastIndex()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
Q_ASSERT(index == RegExpObject::Index_LastIndex);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("source")))->propertyKey(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Source);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("global")))->propertyKey(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Global);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase")))->propertyKey(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_IgnoreCase);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline")))->propertyKey(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Multiline);
- jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic, objectPrototype());
- internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
+ jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic->d());
+ classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
- ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index);
+ ic = classes[Class_ArrayObject]->addMember(id_index()->propertyKey(), Attr_Data, &index);
Q_ASSERT(index == RegExpObject::Index_ArrayIndex);
- internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index);
+ classes[Class_RegExpExecArray] = ic->addMember(id_input()->propertyKey(), Attr_Data, &index);
Q_ASSERT(index == RegExpObject::Index_ArrayInput);
ic = newInternalClass(ErrorObject::staticVTable(), nullptr);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("stack")))->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_Stack);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_FileName);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index);
- internalClasses[EngineBase::Class_ErrorObject] = ic;
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index);
+ classes[Class_ErrorObject] = ic->d();
Q_ASSERT(index == ErrorObject::Index_LineNumber);
- internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
+ classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_Message);
ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember(id_constructor()->propertyKey(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Constructor);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Message);
- internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index);
+ classes[Class_ErrorProto] = ic->addMember(id_name()->propertyKey(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Name);
- jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack);
- getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0));
+ classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable());
+
+ jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0);
- jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto], objectPrototype());
- jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
+ jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]);
+ ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d());
+ jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d());
+ jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d());
+ jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d());
+ jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d());
+ jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d());
+ jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d());
- jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>();
- Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d());
+ jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
+ Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d());
+#if QT_CONFIG(qml_sequence_object)
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
- jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this)));
+ jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
+#endif
ExecutionContext *global = rootContext();
- jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global);
- jsObjects[String_Ctor] = memoryManager->allocObject<StringCtor>(global);
- jsObjects[Number_Ctor] = memoryManager->allocObject<NumberCtor>(global);
- jsObjects[Boolean_Ctor] = memoryManager->allocObject<BooleanCtor>(global);
- jsObjects[Array_Ctor] = memoryManager->allocObject<ArrayCtor>(global);
- jsObjects[Function_Ctor] = memoryManager->allocObject<FunctionCtor>(global);
- jsObjects[Date_Ctor] = memoryManager->allocObject<DateCtor>(global);
- jsObjects[RegExp_Ctor] = memoryManager->allocObject<RegExpCtor>(global);
- jsObjects[Error_Ctor] = memoryManager->allocObject<ErrorCtor>(global);
- jsObjects[EvalError_Ctor] = memoryManager->allocObject<EvalErrorCtor>(global);
- jsObjects[RangeError_Ctor] = memoryManager->allocObject<RangeErrorCtor>(global);
- jsObjects[ReferenceError_Ctor] = memoryManager->allocObject<ReferenceErrorCtor>(global);
- jsObjects[SyntaxError_Ctor] = memoryManager->allocObject<SyntaxErrorCtor>(global);
- jsObjects[TypeError_Ctor] = memoryManager->allocObject<TypeErrorCtor>(global);
- jsObjects[URIError_Ctor] = memoryManager->allocObject<URIErrorCtor>(global);
+
+ jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global);
+ jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global);
+ jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global);
+ jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(global);
+ jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(global);
+ jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(global);
+ jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(global);
+ jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global);
+ jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(global);
+ jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(global);
+ jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(global);
+ jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(global);
+ jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(global);
+ jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(global);
+ jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(global);
+ jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global);
+ jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global);
+ jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>();
+ jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype()));
+
+ str = newString(QStringLiteral("get [Symbol.species]"));
+ jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor());
static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor());
+ static_cast<SymbolPrototype *>(symbolPrototype())->init(this, symbolCtor());
static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor());
static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor());
static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor());
static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this);
static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor());
static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor());
+ static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor());
static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor());
static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor());
static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor());
@@ -383,25 +466,47 @@ ExecutionEngine::ExecutionEngine()
static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor());
static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor());
+ static_cast<IteratorPrototype *>(iteratorPrototype())->init(this);
+ static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this);
+ static_cast<MapIteratorPrototype *>(mapIteratorPrototype())->init(this);
+ static_cast<SetIteratorPrototype *>(setIteratorPrototype())->init(this);
+ static_cast<ArrayIteratorPrototype *>(arrayIteratorPrototype())->init(this);
+ static_cast<StringIteratorPrototype *>(stringIteratorPrototype())->init(this);
+
static_cast<VariantPrototype *>(variantPrototype())->init();
+
+#if QT_CONFIG(qml_sequence_object)
sequencePrototype()->cast<SequencePrototype>()->init();
+#endif
+
+ jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global);
+ jsObjects[MapProto] = memoryManager->allocate<MapPrototype>();
+ static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor());
+ jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global);
+ jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
+ static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
// typed arrays
- jsObjects[ArrayBuffer_Ctor] = memoryManager->allocObject<ArrayBufferCtor>(global);
- jsObjects[ArrayBufferProto] = memoryManager->allocObject<ArrayBufferPrototype>();
+ jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global);
+ jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>();
static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor());
- jsObjects[DataView_Ctor] = memoryManager->allocObject<DataViewCtor>(global);
- jsObjects[DataViewProto] = memoryManager->allocObject<DataViewPrototype>();
+ jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global);
+ jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>();
static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor());
jsObjects[ValueTypeProto] = (Heap::Base *) nullptr;
jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr;
+ jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global);
+ jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>();
+ static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype())
+ ->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor()));
+
for (int i = 0; i < Heap::TypedArray::NTypes; ++i) {
- static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocObject<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
- static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocObject<TypedArrayPrototype>(Heap::TypedArray::Type(i));
+ static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
+ static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i));
typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>()));
}
@@ -413,6 +518,7 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor());
globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Symbol"), *symbolCtor());
FunctionObject *numberObject = numberCtor();
globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject);
globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor());
@@ -430,18 +536,23 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor());
+
for (int i = 0; i < Heap::TypedArray::NTypes; ++i)
globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name())->toQString(), typedArrayCtors[i]);
ScopedObject o(scope);
- globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocObject<MathObject>()));
- globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocObject<JsonObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext())));
globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue());
globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN()));
globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY));
- jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global);
+ jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global);
globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction());
// ES6: 20.1.2.12 & 20.1.2.13:
@@ -453,11 +564,8 @@ ExecutionEngine::ExecutionEngine()
Scope scope(this);
ScopedString pi(scope, newIdentifier(piString));
ScopedString pf(scope, newIdentifier(pfString));
- ExecutionContext *global = rootContext();
- ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(global, pi, GlobalFunctions::method_parseInt));
- ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(global, pf, GlobalFunctions::method_parseFloat));
- parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2));
- parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1));
+ ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(this, pi, GlobalFunctions::method_parseInt, 2));
+ ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(this, pf, GlobalFunctions::method_parseFloat, 1));
globalObject->defineDefaultProperty(piString, parseIntFn);
globalObject->defineDefaultProperty(pfString, parseFloatFn);
numberObject->defineDefaultProperty(piString, parseIntFn);
@@ -473,8 +581,16 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
- ScopedString name(scope, newString(QStringLiteral("thrower")));
- jsObjects[ThrowerObject] = FunctionObject::createBuiltinFunction(global, name, ::throwTypeError);
+ ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError));
+ t->defineReadonlyProperty(id_length(), Primitive::fromInt32(0));
+ t->setInternalClass(t->internalClass()->frozen());
+ jsObjects[ThrowerObject] = t;
+
+ ScopedProperty pd(scope);
+ pd->value = thrower();
+ pd->set = thrower();
+ functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
+ functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
}
ExecutionEngine::~ExecutionEngine()
@@ -487,8 +603,6 @@ ExecutionEngine::~ExecutionEngine()
while (!compilationUnits.isEmpty())
(*compilationUnits.begin())->unlink();
- internalClasses[Class_Empty]->destroy();
- delete classPool;
delete bumperPointerAllocator;
delete regExpCache;
delete regExpAllocator;
@@ -500,6 +614,11 @@ ExecutionEngine::~ExecutionEngine()
delete [] argumentsAccessors;
}
+ExecutionContext *ExecutionEngine::currentContext() const
+{
+ return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
+}
+
#if QT_CONFIG(qml_debug)
void ExecutionEngine::setDebugger(Debugging::Debugger *debugger)
{
@@ -521,59 +640,71 @@ void ExecutionEngine::initRootContext()
r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
r->d()->activation.set(this, globalObject->d());
jsObjects[RootContext] = r;
+ jsObjects[ScriptContext] = r;
jsObjects[IntegerNull] = Encode((int)0);
}
-InternalClass *ExecutionEngine::newClass(const InternalClass &other)
+Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other)
{
- return new (classPool) InternalClass(other);
+ Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>();
+ ic->init(other);
+ return ic;
}
-InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
+Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
{
- return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : nullptr);
+ Scope scope(this);
+ Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable));
+ return ic->changePrototype(prototype ? prototype->d() : nullptr);
}
Heap::Object *ExecutionEngine::newObject()
{
- return memoryManager->allocObject<Object>();
+ return memoryManager->allocate<Object>();
}
-Heap::Object *ExecutionEngine::newObject(InternalClass *internalClass, QV4::Object *prototype)
+Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass)
{
- return memoryManager->allocObject<Object>(internalClass, prototype);
+ return memoryManager->allocObject<Object>(internalClass);
}
Heap::String *ExecutionEngine::newString(const QString &s)
{
- Scope scope(this);
- return ScopedString(scope, memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s))->d();
+ return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s);
}
Heap::String *ExecutionEngine::newIdentifier(const QString &text)
{
- return identifierTable->insertString(text);
+ Scope scope(this);
+ ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text));
+ s->toPropertyKey();
+ return s->d();
}
Heap::Object *ExecutionEngine::newStringObject(const String *string)
{
- return memoryManager->allocObject<StringObject>(string);
+ return memoryManager->allocate<StringObject>(string);
+}
+
+Heap::Object *ExecutionEngine::newSymbolObject(const Symbol *symbol)
+{
+ return memoryManager->allocObject<SymbolObject>(classes[Class_SymbolObject], symbol);
}
Heap::Object *ExecutionEngine::newNumberObject(double value)
{
- return memoryManager->allocObject<NumberObject>(value);
+ return memoryManager->allocate<NumberObject>(value);
}
Heap::Object *ExecutionEngine::newBooleanObject(bool b)
{
- return memoryManager->allocObject<BooleanObject>(b);
+ return memoryManager->allocate<BooleanObject>(b);
}
Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
{
Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>());
+ ScopedArrayObject object(scope, memoryManager->allocate<ArrayObject>());
if (count) {
if (count < 0x1000)
@@ -586,7 +717,7 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length)
{
Scope scope(this);
- ScopedArrayObject a(scope, memoryManager->allocObject<ArrayObject>());
+ ScopedArrayObject a(scope, memoryManager->allocate<ArrayObject>());
if (length) {
size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value);
@@ -607,45 +738,41 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng
Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list)
{
- Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(list));
- return object->d();
+ return memoryManager->allocate<ArrayObject>(list);
}
-Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *internalClass, Object *prototype)
+Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass)
{
- Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(internalClass, prototype));
- return object->d();
+ return memoryManager->allocObject<ArrayObject>(internalClass);
}
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array)
{
- return memoryManager->allocObject<ArrayBuffer>(array);
+ return memoryManager->allocate<ArrayBuffer>(array);
}
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length)
{
- return memoryManager->allocObject<ArrayBuffer>(length);
+ return memoryManager->allocate<ArrayBuffer>(length);
}
Heap::DateObject *ExecutionEngine::newDateObject(const Value &value)
{
- return memoryManager->allocObject<DateObject>(value);
+ return memoryManager->allocate<DateObject>(value);
}
Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt)
{
Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(dt));
+ Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt));
return object->d();
}
Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t)
{
Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t));
+ Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t));
return object->d();
}
@@ -662,12 +789,12 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int
Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re)
{
- return memoryManager->allocObject<RegExpObject>(re);
+ return memoryManager->allocate<RegExpObject>(re);
}
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
{
- return memoryManager->allocObject<RegExpObject>(re);
+ return memoryManager->allocate<RegExpObject>(re);
}
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
@@ -714,16 +841,31 @@ Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message)
Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v)
{
- return memoryManager->allocObject<VariantObject>(v);
+ return memoryManager->allocate<VariantObject>(v);
}
-Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o)
+Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o)
{
Scope scope(this);
- ScopedObject obj(scope, memoryManager->allocObject<ForEachIteratorObject>(o));
+ ScopedObject obj(scope, memoryManager->allocate<ForInIteratorObject>(o));
return obj->d();
}
+Heap::Object *ExecutionEngine::newMapIteratorObject(Object *o)
+{
+ return memoryManager->allocate<MapIteratorObject>(o->d(), this);
+}
+
+Heap::Object *ExecutionEngine::newSetIteratorObject(Object *o)
+{
+ return memoryManager->allocate<SetIteratorObject>(o->d(), this);
+}
+
+Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o)
+{
+ return memoryManager->allocate<ArrayIteratorObject>(o->d(), this);
+}
+
Heap::QmlContext *ExecutionEngine::qmlContext() const
{
if (!currentStackFrame)
@@ -761,37 +903,6 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const
return ctx->qml()->context->contextData();
}
-QString CppStackFrame::source() const
-{
- return v4Function ? v4Function->sourceFile() : QString();
-}
-
-QString CppStackFrame::function() const
-{
- return v4Function ? v4Function->name()->toQString() : QString();
-}
-
-int CppStackFrame::lineNumber() const
-{
- if (!v4Function)
- return -1;
-
- auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
- return entry.codeOffset < offset;
- };
-
- const QV4::CompiledData::Function *cf = v4Function->compiledFunction;
- uint offset = instructionPointer;
- const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable();
- uint nLineNumbers = cf->nLineNumbers;
- const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1;
- return line->line;
-}
-
-ReturnedValue CppStackFrame::thisObject() const {
- return jsFrame->thisObject.asReturnedValue();
-}
-
StackTrace ExecutionEngine::stackTrace(int frameLimit) const
{
Scope scope(const_cast<ExecutionEngine *>(this));
@@ -891,16 +1002,14 @@ void ExecutionEngine::requireArgumentsAccessors(int n)
}
ExecutionContext *global = rootContext();
for (int i = oldSize; i < nArgumentsAccessors; ++i) {
- argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocObject<ArgumentsGetterFunction>(global, i));
- argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocObject<ArgumentsSetterFunction>(global, i));
+ argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocate<ArgumentsGetterFunction>(global, i));
+ argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocate<ArgumentsSetterFunction>(global, i));
}
}
}
void ExecutionEngine::markObjects(MarkStack *markStack)
{
- identifierTable->mark(markStack);
-
for (int i = 0; i < nArgumentsAccessors; ++i) {
const Property &pd = argumentsAccessors[i];
if (Heap::FunctionObject *getter = pd.getter())
@@ -909,9 +1018,13 @@ void ExecutionEngine::markObjects(MarkStack *markStack)
setter->mark(markStack);
}
- classPool->markObjects(markStack);
+ for (int i = 0; i < NClasses; ++i)
+ if (classes[i])
+ classes[i]->mark(markStack);
markStack->drain();
+ identifierTable->markObjects(markStack);
+
for (auto compilationUnit: compilationUnits) {
compilationUnit->markObjects(markStack);
markStack->drain();
@@ -1053,12 +1166,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
error.setColumn(frame.column);
}
QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
- if (!!errorObj && errorObj->asSyntaxError()) {
- QV4::ScopedString m(scope, newString(QStringLiteral("message")));
- QV4::ScopedValue v(scope, errorObj->get(m));
- error.setDescription(v->toQStringNoThrow());
- } else
- error.setDescription(exception->toQStringNoThrow());
+ error.setDescription(exception->toQStringNoThrow());
return error;
}
@@ -1117,8 +1225,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return v->toVariant();
} else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) {
return l->toVariant();
- } else if (object->isListType())
+#if QT_CONFIG(qml_sequence_object)
+ } else if (object->isListType()) {
return QV4::SequencePrototype::toVariant(object);
+#endif
+ }
}
if (value.as<ArrayObject>()) {
@@ -1128,7 +1239,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
uint length = a->getLength();
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
for (uint ii = 0; ii < length; ++ii) {
- qobjectWrapper = a->getIndexed(ii);
+ qobjectWrapper = a->get(ii);
if (!!qobjectWrapper) {
list << qobjectWrapper->object();
} else {
@@ -1141,10 +1252,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
}
+#if QT_CONFIG(qml_sequence_object)
bool succeeded = false;
QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded);
if (succeeded)
return retn;
+#endif
}
if (value.isUndefined())
@@ -1164,8 +1277,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return str.at(0);
return str;
}
+#if QT_CONFIG(qml_locale)
if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>())
return *ld->d()->locale;
+#endif
if (const QV4::DateObject *d = value.as<DateObject>())
return d->toQDateTime();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
@@ -1211,7 +1326,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
- v = a->getIndexed(ii);
+ v = a->get(ii);
list << ::toVariant(e, v, -1, /*createJSValueForObjects*/false, visitedObjects);
}
@@ -1260,9 +1375,6 @@ static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QV
QV4::ScopedValue v(scope);
for (QVariantMap::const_iterator iter = map.begin(), cend = map.end(); iter != cend; ++iter) {
s = e->newString(iter.key());
- uint idx = s->asArrayIndex();
- if (idx > 16 && (!o->arrayData() || idx > o->arrayData()->length() * 2))
- o->initSparseArray();
o->put(s, (v = e->fromVariant(iter.value())));
}
return o.asReturnedValue();
@@ -1321,6 +1433,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr)));
case QMetaType::QObjectStar:
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
+#if QT_CONFIG(qml_sequence_object)
case QMetaType::QStringList:
{
bool succeeded = false;
@@ -1330,6 +1443,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return retn->asReturnedValue();
return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr)));
}
+#endif
case QMetaType::QVariantList:
return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
case QMetaType::QVariantMap:
@@ -1340,8 +1454,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr));
case QMetaType::QJsonArray:
return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr));
+#if QT_CONFIG(qml_locale)
case QMetaType::QLocale:
return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
+#endif
default:
break;
}
@@ -1381,10 +1497,12 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
if (objOk)
return QV4::QObjectWrapper::wrap(this, obj);
+#if QT_CONFIG(qml_sequence_object)
bool succeeded = false;
QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded));
if (succeeded)
return retn->asReturnedValue();
+#endif
if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type))
return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type);
@@ -1428,11 +1546,13 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian
QV4::Scope scope(v4);
QV4::ScopedObject o(scope, v4->newObject());
QV4::ScopedString s(scope);
+ QV4::ScopedPropertyKey key(scope);
QV4::ScopedValue v(scope);
for (QVariantMap::const_iterator it = vmap.constBegin(), cend = vmap.constEnd(); it != cend; ++it) {
s = v4->newIdentifier(it.key());
+ key = s->propertyKey();
v = variantToJS(v4, it.value());
- uint idx = s->asArrayIndex();
+ uint idx = key->asArrayIndex();
if (idx < UINT_MAX)
o->arraySet(idx, v);
else
@@ -1707,7 +1827,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
} else if (Object *o = value->objectValue()) {
// Look in the prototype chain.
QV4::Scope scope(this);
- QV4::ScopedObject proto(scope, o->prototype());
+ QV4::ScopedObject proto(scope, o->getPrototypeOf());
while (proto) {
bool canCast = false;
if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
@@ -1728,7 +1848,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
*reinterpret_cast<void* *>(data) = var.data();
return true;
}
- proto = proto->prototype();
+ proto = proto->getPrototypeOf();
}
}
} else if (value->isNull() && name.endsWith('*')) {
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index c7fb743088..fc0c93eaad 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -56,7 +56,6 @@
#include <private/qintrusivelist_p.h>
#include "qv4enginebase_p.h"
-
#ifndef V4_BOOTSTRAP
# include "qv4function_p.h"
# include <private/qv8engine_p.h>
@@ -88,33 +87,6 @@ struct CompilationUnit;
}
struct Function;
-struct InternalClass;
-struct InternalClassPool;
-
-struct Q_QML_EXPORT CppStackFrame {
- CppStackFrame *parent;
- Function *v4Function;
- CallData *jsFrame;
- const Value *originalArguments;
- int originalArgumentsCount;
- int instructionPointer;
-
- QString source() const;
- QString function() const;
- inline QV4::ExecutionContext *context() const {
- return static_cast<ExecutionContext *>(&jsFrame->context);
- }
- int lineNumber() const;
-
- inline QV4::Heap::CallContext *callContext() const {
- Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\
- while (ctx->type != Heap::ExecutionContext::Type_CallContext)
- ctx = ctx->outer;
- return static_cast<Heap::CallContext *>(ctx);
- }
- ReturnedValue thisObject() const;
-};
-
struct Q_QML_EXPORT ExecutionEngine : public EngineBase
@@ -142,8 +114,6 @@ public:
QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) {
Value *ptr = jsStackTop;
jsStackTop = ptr + nValues;
- for (int i = 0; i < nValues; ++i)
- ptr[i] = Primitive::undefinedValue();
return ptr;
}
@@ -153,22 +123,27 @@ public:
QJSEngine *jsEngine() const;
QQmlEngine *qmlEngine() const;
#else // !V4_BOOTSTRAP
- QJSEngine *jsEngine() const { return v8Engine->publicEngine(); }
+ QJSEngine *jsEngine() const { return publicEngine; }
QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; }
#endif // V4_BOOTSTRAP
QV8Engine *v8Engine;
+ QJSEngine *publicEngine;
enum JSObjects {
RootContext,
+ ScriptContext,
IntegerNull, // Has to come after the RootContext to make the context stack safe
ObjectProto,
+ SymbolProto,
ArrayProto,
+ ArrayProtoValues,
PropertyListProto,
StringProto,
NumberProto,
BooleanProto,
DateProto,
FunctionProto,
+ GeneratorProto,
RegExpProto,
ErrorProto,
EvalErrorProto,
@@ -178,18 +153,31 @@ public:
TypeErrorProto,
URIErrorProto,
VariantProto,
+#if QT_CONFIG(qml_sequence_object)
SequenceProto,
+#endif
ArrayBufferProto,
DataViewProto,
+ SetProto,
+ MapProto,
+ IntrinsicTypedArrayProto,
ValueTypeProto,
SignalHandlerProto,
+ IteratorProto,
+ ForInIteratorProto,
+ SetIteratorProto,
+ MapIteratorProto,
+ ArrayIteratorProto,
+ StringIteratorProto,
Object_Ctor,
String_Ctor,
+ Symbol_Ctor,
Number_Ctor,
Boolean_Ctor,
Array_Ctor,
Function_Ctor,
+ GeneratorFunction_Ctor,
Date_Ctor,
RegExp_Ctor,
Error_Ctor,
@@ -201,6 +189,11 @@ public:
URIError_Ctor,
ArrayBuffer_Ctor,
DataView_Ctor,
+ Set_Ctor,
+ Map_Ctor,
+ IntrinsicTypedArray_Ctor,
+
+ GetSymbolSpecies,
Eval_Function,
GetStack_Function,
@@ -211,12 +204,16 @@ public:
enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency
ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); }
+ ExecutionContext *scriptContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + ScriptContext); }
+ void setScriptContext(ReturnedValue c) { jsObjects[ScriptContext] = c; }
FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); }
FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); }
+ FunctionObject *symbolCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Symbol_Ctor); }
FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); }
FunctionObject *booleanCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Boolean_Ctor); }
FunctionObject *arrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Array_Ctor); }
FunctionObject *functionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Function_Ctor); }
+ FunctionObject *generatorFunctionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + GeneratorFunction_Ctor); }
FunctionObject *dateCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Date_Ctor); }
FunctionObject *regExpCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RegExp_Ctor); }
FunctionObject *errorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Error_Ctor); }
@@ -228,16 +225,24 @@ public:
FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); }
FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); }
FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); }
+ FunctionObject *setCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Set_Ctor); }
+ FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); }
+ FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); }
FunctionObject *typedArrayCtors;
+ FunctionObject *getSymbolSpecies() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetSymbolSpecies); }
+
Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); }
+ Object *symbolPrototype() const { return reinterpret_cast<Object *>(jsObjects + SymbolProto); }
Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); }
+ Object *arrayProtoValues() const { return reinterpret_cast<Object *>(jsObjects + ArrayProtoValues); }
Object *propertyListPrototype() const { return reinterpret_cast<Object *>(jsObjects + PropertyListProto); }
Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); }
Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); }
Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); }
Object *datePrototype() const { return reinterpret_cast<Object *>(jsObjects + DateProto); }
Object *functionPrototype() const { return reinterpret_cast<Object *>(jsObjects + FunctionProto); }
+ Object *generatorPrototype() const { return reinterpret_cast<Object *>(jsObjects + GeneratorProto); }
Object *regExpPrototype() const { return reinterpret_cast<Object *>(jsObjects + RegExpProto); }
Object *errorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ErrorProto); }
Object *evalErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + EvalErrorProto); }
@@ -247,16 +252,26 @@ public:
Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); }
Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); }
Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); }
+#if QT_CONFIG(qml_sequence_object)
Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); }
+#endif
Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); }
Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); }
+ Object *setPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetProto); }
+ Object *mapPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapProto); }
+ Object *intrinsicTypedArrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + IntrinsicTypedArrayProto); }
Object *typedArrayPrototype;
Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); }
Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); }
+ Object *iteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + IteratorProto); }
+ Object *forInIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ForInIteratorProto); }
+ Object *setIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetIteratorProto); }
+ Object *mapIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapIteratorProto); }
+ Object *arrayIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayIteratorProto); }
+ Object *stringIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringIteratorProto); }
- InternalClassPool *classPool;
EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); }
FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); }
FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); }
@@ -273,6 +288,8 @@ public:
String_boolean,
String_number,
String_string,
+ String_default,
+ String_symbol,
String_object,
String_function,
String_length,
@@ -301,10 +318,31 @@ public:
String_byteOffset,
String_buffer,
String_lastIndex,
+ String_next,
+ String_done,
+ String_return,
+
NJSStrings
};
Value *jsStrings;
+ enum JSSymbols {
+ Symbol_hasInstance,
+ Symbol_isConcatSpreadable,
+ Symbol_iterator,
+ Symbol_match,
+ Symbol_replace,
+ Symbol_search,
+ Symbol_species,
+ Symbol_split,
+ Symbol_toPrimitive,
+ Symbol_toStringTag,
+ Symbol_unscopables,
+ Symbol_revokableProxy,
+ NJSSymbols
+ };
+ Value *jsSymbols;
+
String *id_empty() const { return reinterpret_cast<String *>(jsStrings + String_Empty); }
String *id_undefined() const { return reinterpret_cast<String *>(jsStrings + String_undefined); }
String *id_null() const { return reinterpret_cast<String *>(jsStrings + String_null); }
@@ -313,6 +351,8 @@ public:
String *id_boolean() const { return reinterpret_cast<String *>(jsStrings + String_boolean); }
String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); }
String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); }
+ String *id_default() const { return reinterpret_cast<String *>(jsStrings + String_default); }
+ String *id_symbol() const { return reinterpret_cast<String *>(jsStrings + String_symbol); }
String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); }
String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); }
String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); }
@@ -341,6 +381,22 @@ public:
String *id_byteOffset() const { return reinterpret_cast<String *>(jsStrings + String_byteOffset); }
String *id_buffer() const { return reinterpret_cast<String *>(jsStrings + String_buffer); }
String *id_lastIndex() const { return reinterpret_cast<String *>(jsStrings + String_lastIndex); }
+ String *id_next() const { return reinterpret_cast<String *>(jsStrings + String_next); }
+ String *id_done() const { return reinterpret_cast<String *>(jsStrings + String_done); }
+ String *id_return() const { return reinterpret_cast<String *>(jsStrings + String_return); }
+
+ Symbol *symbol_hasInstance() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_hasInstance); }
+ Symbol *symbol_isConcatSpreadable() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_isConcatSpreadable); }
+ Symbol *symbol_iterator() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_iterator); }
+ Symbol *symbol_match() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_match); }
+ Symbol *symbol_replace() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_replace); }
+ Symbol *symbol_search() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_search); }
+ Symbol *symbol_species() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_species); }
+ Symbol *symbol_split() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_split); }
+ Symbol *symbol_toPrimitive() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toPrimitive); }
+ Symbol *symbol_toStringTag() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toStringTag); }
+ Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); }
+ Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); }
#ifndef V4_BOOTSTRAP
QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits;
@@ -372,9 +428,9 @@ public:
const bool m_canAllocateExecutableMemory;
#endif
- int internalClassIdCount = 0;
+ quintptr protoIdCount = 1;
- ExecutionEngine();
+ ExecutionEngine(QJSEngine *jsEngine = nullptr);
~ExecutionEngine();
#if !QT_CONFIG(qml_debug)
@@ -391,29 +447,28 @@ public:
void setProfiler(Profiling::Profiler *profiler);
#endif // QT_CONFIG(qml_debug)
- void setCurrentContext(Heap::ExecutionContext *context);
- ExecutionContext *currentContext() const {
- return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
- }
+ ExecutionContext *currentContext() const;
- int newInternalClassId() { return ++internalClassIdCount; }
+ // ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast
+ quintptr newProtoId() { return (protoIdCount += 2); }
- InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
+ Heap::InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
Heap::Object *newObject();
- Heap::Object *newObject(InternalClass *internalClass, Object *prototype);
+ Heap::Object *newObject(Heap::InternalClass *internalClass);
Heap::String *newString(const QString &s = QString());
Heap::String *newIdentifier(const QString &text);
Heap::Object *newStringObject(const String *string);
+ Heap::Object *newSymbolObject(const Symbol *symbol);
Heap::Object *newNumberObject(double value);
Heap::Object *newBooleanObject(bool b);
Heap::ArrayObject *newArrayObject(int count = 0);
Heap::ArrayObject *newArrayObject(const Value *values, int length);
Heap::ArrayObject *newArrayObject(const QStringList &list);
- Heap::ArrayObject *newArrayObject(InternalClass *ic, Object *prototype);
+ Heap::ArrayObject *newArrayObject(Heap::InternalClass *ic);
Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array);
Heap::ArrayBuffer *newArrayBuffer(size_t length);
@@ -437,7 +492,10 @@ public:
Heap::Object *newVariantObject(const QVariant &v);
- Heap::Object *newForEachIteratorObject(Object *o);
+ Heap::Object *newForInIteratorObject(Object *o);
+ Heap::Object *newSetIteratorObject(Object *o);
+ Heap::Object *newMapIteratorObject(Object *o);
+ Heap::Object *newArrayIteratorObject(Object *o);
Heap::QmlContext *qmlContext() const;
QObject *qmlScopeObject() const;
@@ -453,7 +511,7 @@ public:
void initRootContext();
- InternalClass *newClass(const InternalClass &other);
+ Heap::InternalClass *newClass(Heap::InternalClass *other);
StackTrace exceptionStackTrace;
@@ -492,7 +550,7 @@ public:
if (!m_canAllocateExecutableMemory)
return false;
if (f)
- return f->interpreterCallCount >= jitCallCountThreshold;
+ return !f->isGenerator() && f->interpreterCallCount >= jitCallCountThreshold;
return true;
#else
Q_UNUSED(f);
@@ -502,6 +560,7 @@ public:
QV4::ReturnedValue global();
+ double localTZA = 0.0; // local timezone, initialized at startup
private:
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
@@ -521,11 +580,6 @@ struct NoThrowEngine;
#endif
-inline void ExecutionEngine::setCurrentContext(Heap::ExecutionContext *context)
-{
- currentStackFrame->jsFrame->context = context;
-}
-
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
index 59fb4a564a..3e89e57abb 100644
--- a/src/qml/jsruntime/qv4enginebase_p.h
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -88,7 +88,7 @@ struct Q_QML_EXPORT EngineBase {
// Exception handling
Value *exceptionValue = nullptr;
- enum {
+ enum InternalClassType {
Class_Empty,
Class_String,
Class_MemberData,
@@ -96,11 +96,18 @@ struct Q_QML_EXPORT EngineBase {
Class_SparseArrayData,
Class_ExecutionContext,
Class_CallContext,
+ Class_QmlContext,
Class_Object,
Class_ArrayObject,
Class_FunctionObject,
+ Class_GeneratorFunction,
+ Class_GeneratorObject,
Class_StringObject,
+ Class_SymbolObject,
Class_ScriptFunction,
+ Class_ConstructorFunction,
+ Class_MemberFunction,
+ Class_MemberGeneratorFunction,
Class_ObjectProto,
Class_RegExp,
Class_RegExpObject,
@@ -111,10 +118,12 @@ struct Q_QML_EXPORT EngineBase {
Class_ErrorObjectWithMessage,
Class_ErrorProto,
Class_QmlContextWrapper,
- Class_QmlContext,
+ Class_ProxyObject,
+ Class_Symbol,
NClasses
};
- InternalClass *internalClasses[NClasses];
+ Heap::InternalClass *classes[NClasses];
+ Heap::InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; }
};
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(pop)
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index 90e158ba37..e8b4e04e83 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -47,10 +47,6 @@
#include "qv4string_p.h"
#include <private/qv4mm_p.h>
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
#include <qv4codegen_p.h>
#ifndef Q_OS_WIN
@@ -74,7 +70,7 @@ void Heap::ErrorObject::init()
Scope scope(internalClass->engine);
Scoped<QV4::ErrorObject> e(scope, this);
- if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto])
+ if (internalClass == scope.engine->internalClasses(EngineBase::Class_ErrorProto))
return;
setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d());
@@ -233,13 +229,13 @@ void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name)
Heap::FunctionObject::init(scope, name);
}
-ReturnedValue ErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Value v = argc ? *argv : Primitive::undefinedValue();
return ErrorObject::create<ErrorObject>(f->engine(), v)->asReturnedValue();
}
-ReturnedValue ErrorCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue ErrorCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
return f->callAsConstructor(argv, argc);
}
@@ -249,7 +245,7 @@ void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("EvalError"));
}
-ReturnedValue EvalErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue EvalErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Value v = argc ? *argv : Primitive::undefinedValue();
return ErrorObject::create<EvalErrorObject>(f->engine(), v)->asReturnedValue();
@@ -260,7 +256,7 @@ void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("RangeError"));
}
-ReturnedValue RangeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue RangeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Value v = argc ? *argv : Primitive::undefinedValue();
return ErrorObject::create<RangeErrorObject>(f->engine(), v)->asReturnedValue();
@@ -271,7 +267,7 @@ void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError"));
}
-ReturnedValue ReferenceErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ReferenceErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Value v = argc ? *argv : Primitive::undefinedValue();
return ErrorObject::create<ReferenceErrorObject>(f->engine(), v)->asReturnedValue();
@@ -282,7 +278,7 @@ void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError"));
}
-ReturnedValue SyntaxErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue SyntaxErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Value v = argc ? *argv : Primitive::undefinedValue();
return ErrorObject::create<SyntaxErrorObject>(f->engine(), v)->asReturnedValue();
@@ -293,7 +289,7 @@ void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("TypeError"));
}
-ReturnedValue TypeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue TypeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Value v = argc ? *argv : Primitive::undefinedValue();
return ErrorObject::create<TypeErrorObject>(f->engine(), v)->asReturnedValue();
@@ -304,7 +300,7 @@ void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope)
Heap::ErrorCtor::init(scope, QStringLiteral("URIError"));
}
-ReturnedValue URIErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue URIErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Value v = argc ? *argv : Primitive::undefinedValue();
return ErrorObject::create<URIErrorObject>(f->engine(), v)->asReturnedValue();
@@ -316,7 +312,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He
ScopedString s(scope);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
obj->setProperty(Index_Constructor, ctor->d());
obj->setProperty(Index_Message, engine->id_empty()->d());
obj->setProperty(Index_Name, engine->newString(QString::fromLatin1(ErrorObject::className(t))));
diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h
index 6b578e8c38..90ef4dd842 100644
--- a/src/qml/jsruntime/qv4errorobject_p.h
+++ b/src/qml/jsruntime/qv4errorobject_p.h
@@ -180,7 +180,7 @@ struct ErrorObject: Object {
template<>
inline const ErrorObject *Value::as() const {
- return isManaged() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr;
}
struct EvalErrorObject: ErrorObject {
@@ -229,50 +229,50 @@ struct ErrorCtor: FunctionObject
{
V4_OBJECT2(ErrorCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct EvalErrorCtor: ErrorCtor
{
V4_OBJECT2(EvalErrorCtor, ErrorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct RangeErrorCtor: ErrorCtor
{
V4_OBJECT2(RangeErrorCtor, ErrorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct ReferenceErrorCtor: ErrorCtor
{
V4_OBJECT2(ReferenceErrorCtor, ErrorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct SyntaxErrorCtor: ErrorCtor
{
V4_OBJECT2(SyntaxErrorCtor, ErrorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct TypeErrorCtor: ErrorCtor
{
V4_OBJECT2(TypeErrorCtor, ErrorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct URIErrorCtor: ErrorCtor
{
V4_OBJECT2(URIErrorCtor, ErrorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
@@ -328,25 +328,26 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError()
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) {
- InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), message);
+ EngineBase::InternalClassType klass = message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scope scope(e);
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), message);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v);
+ EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), v);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v, filename, line, column);
+ EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), v, filename, line, column);
}
diff --git a/src/qml/jsruntime/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp
new file mode 100644
index 0000000000..55b7407000
--- /dev/null
+++ b/src/qml/jsruntime/qv4estable.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4estable_p.h"
+
+using namespace QV4;
+
+// The ES spec requires that Map/Set be implemented using a data structure that
+// is a little different from most; it requires nonlinear access, and must also
+// preserve the order of insertion of items in a deterministic way.
+//
+// This class implements those requirements, except for fast access: that
+// will be addressed in a followup patch.
+
+ESTable::ESTable()
+ : m_capacity(8)
+{
+ m_keys = (Value*)malloc(m_capacity * sizeof(Value));
+ m_values = (Value*)malloc(m_capacity * sizeof(Value));
+ memset(m_keys, 0, m_capacity);
+ memset(m_values, 0, m_capacity);
+}
+
+ESTable::~ESTable()
+{
+ free(m_keys);
+ free(m_values);
+ m_size = 0;
+ m_capacity = 0;
+ m_keys = nullptr;
+ m_values = nullptr;
+}
+
+void ESTable::markObjects(MarkStack *s)
+{
+ for (uint i = 0; i < m_size; ++i) {
+ m_keys[i].mark(s);
+ m_values[i].mark(s);
+ }
+}
+
+// Pretends that there's nothing in the table. Doesn't actually free memory, as
+// it will almost certainly be reused again anyway.
+void ESTable::clear()
+{
+ m_size = 0;
+}
+
+// Update the table to contain \a value for a given \a key. The key is
+// normalized, as required by the ES spec.
+void ESTable::set(const Value &key, const Value &value)
+{
+ for (uint i = 0; i < m_size; ++i) {
+ if (m_keys[i].sameValueZero(key)) {
+ m_values[i] = value;
+ return;
+ }
+ }
+
+ if (m_capacity == m_size) {
+ uint oldCap = m_capacity;
+ m_capacity *= 2;
+ m_keys = (Value*)realloc(m_keys, m_capacity * sizeof(Value));
+ m_values = (Value*)realloc(m_values, m_capacity * sizeof(Value));
+ memset(m_keys + oldCap, 0, m_capacity - oldCap);
+ memset(m_values + oldCap, 0, m_capacity - oldCap);
+ }
+
+ Value nk = key;
+ if (nk.isDouble()) {
+ if (nk.doubleValue() == 0 && std::signbit(nk.doubleValue()))
+ nk = Primitive::fromDouble(+0);
+ }
+
+ m_keys[m_size] = nk;
+ m_values[m_size] = value;
+
+ m_size++;
+}
+
+// Returns true if the table contains \a key, false otherwise.
+bool ESTable::has(const Value &key) const
+{
+ for (uint i = 0; i < m_size; ++i) {
+ if (m_keys[i].sameValueZero(key))
+ return true;
+ }
+
+ return false;
+}
+
+// Fetches the value for the given \a key, and if \a hasValue is passed in,
+// it is set depending on whether or not the given key was found.
+ReturnedValue ESTable::get(const Value &key, bool *hasValue) const
+{
+ for (uint i = 0; i < m_size; ++i) {
+ if (m_keys[i].sameValueZero(key)) {
+ if (hasValue)
+ *hasValue = true;
+ return m_values[i].asReturnedValue();
+ }
+ }
+
+ if (hasValue)
+ *hasValue = false;
+ return Encode::undefined();
+}
+
+// Removes the given \a key from the table
+bool ESTable::remove(const Value &key)
+{
+ bool found = false;
+ uint idx = 0;
+ for (; idx < m_size; ++idx) {
+ if (m_keys[idx].sameValueZero(key)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == true) {
+ memmove(m_keys + idx, m_keys + idx + 1, m_size - idx);
+ memmove(m_values + idx, m_values + idx + 1, m_size - idx);
+ m_size--;
+ }
+ return found;
+}
+
+// Returns the size of the table. Note that the size may not match the underlying allocation.
+uint ESTable::size() const
+{
+ return m_size;
+}
+
+// Retrieves a key and value for a given \a idx, and places them in \a key and
+// \a value. They must be valid pointers.
+void ESTable::iterate(uint idx, Value *key, Value *value)
+{
+ Q_ASSERT(idx < m_size);
+ Q_ASSERT(key);
+ Q_ASSERT(value);
+ *key = m_keys[idx];
+ *value = m_values[idx];
+}
+
diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h
new file mode 100644
index 0000000000..c665467760
--- /dev/null
+++ b/src/qml/jsruntime/qv4estable_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QV4ESTABLE_P_H
+#define QV4ESTABLE_P_H
+
+#include "qv4value_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4
+{
+
+class ESTable
+{
+public:
+ ESTable();
+ ~ESTable();
+
+ void markObjects(MarkStack *s);
+ void clear();
+ void set(const Value &k, const Value &v);
+ bool has(const Value &k) const;
+ ReturnedValue get(const Value &k, bool *hasValue = nullptr) const;
+ bool remove(const Value &k);
+ uint size() const;
+ void iterate(uint idx, Value *k, Value *v);
+
+private:
+ Value *m_keys = nullptr;
+ Value *m_values = nullptr;
+ uint m_size = 0;
+ uint m_capacity = 0;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 6fca9ecd45..5e3860a660 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -47,32 +47,51 @@
#include <private/qv4mm_p.h>
#include <private/qv4identifiertable_p.h>
#include <assembler/MacroAssemblerCodeRef.h>
+#include <private/qv4vme_moth_p.h>
+#include <private/qqmlglobal_p.h>
QT_BEGIN_NAMESPACE
using namespace QV4;
-Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr)
- : compiledFunction(function)
- , compilationUnit(unit)
- , code(codePtr)
- , codeData(function->code())
+ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
+ ExecutionEngine *engine = context->engine();
+ CppStackFrame frame;
+ frame.init(engine, this, argv, argc);
+ frame.setupJSFrame(engine->jsStackTop, Primitive::undefinedValue(), context->d(),
+ thisObject ? *thisObject : Primitive::undefinedValue(),
+ Primitive::undefinedValue());
+
+ frame.push();
+ engine->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result = Moth::VME::exec(&frame, engine);
+
+ frame.pop();
+
+ return result;
+}
+
+Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function)
+ : compiledFunction(function)
+ , compilationUnit(unit)
+ , codeData(function->code())
, jittedCode(nullptr)
, codeRef(nullptr)
, hasQmlDependencies(function->hasQmlDependencies())
{
- Q_UNUSED(engine);
-
- internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
const quint32_le *formalsIndices = compiledFunction->formalsTable();
for (quint32 i = 0; i < compiledFunction->nFormals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+ internalClass = ic->d();
nFormals = compiledFunction->nFormals;
}
@@ -110,20 +129,25 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
}
- internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+ internalClass = engine->internalClasses(EngineBase::Class_CallContext);
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
Scope scope(engine);
ScopedString arg(scope);
for (const QString &parameterName : parameterNames) {
- arg = engine->newString(parameterName);
- internalClass = internalClass->addMember(arg, Attr_NotConfigurable);
+ arg = engine->newIdentifier(parameterName);
+ internalClass = internalClass->addMember(arg->propertyKey(), Attr_NotConfigurable);
}
nFormals = parameters.size();
}
+QQmlSourceLocation Function::sourceLocation() const
+{
+ return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 59a94e5dde..d542ce752f 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -51,10 +51,8 @@
//
#include "qv4global_p.h"
-#include <private/qqmlglobal_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qv4context_p.h>
-#include <private/qv4vme_moth_p.h>
namespace JSC {
class MacroAssemblerCodeRef;
@@ -62,31 +60,29 @@ class MacroAssemblerCodeRef;
QT_BEGIN_NAMESPACE
+struct QQmlSourceLocation;
+
namespace QV4 {
struct Q_QML_EXPORT Function {
const CompiledData::Function *compiledFunction;
CompiledData::CompilationUnit *compilationUnit;
- ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
- return Moth::VME::exec(this, thisObject, argv, argc, context);
- }
+ ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context);
- typedef ReturnedValue (*Code)(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
- Code code;
- const uchar *codeData;
+ const char *codeData;
typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *);
JittedCode jittedCode;
JSC::MacroAssemblerCodeRef *codeRef;
// first nArguments names in internalClass are the actual arguments
- InternalClass *internalClass;
+ Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
bool hasQmlDependencies;
- Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr);
+ Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
~Function();
// used when dynamically assigning signal handlers (QQmlConnection)
@@ -98,13 +94,11 @@ struct Q_QML_EXPORT Function {
inline QString sourceFile() const { return compilationUnit->fileName(); }
inline QUrl finalUrl() const { return compilationUnit->finalUrl(); }
- inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; }
inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; }
+ inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; }
+ inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; }
- QQmlSourceLocation sourceLocation() const
- {
- return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
- }
+ QQmlSourceLocation sourceLocation() const;
Function *nestedFunction() const
{
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 83608070ec..2aca7c2849 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -41,6 +41,7 @@
#include "qv4objectproto_p.h"
#include "qv4stringobject_p.h"
#include "qv4function_p.h"
+#include "qv4symbol_p.h"
#include <private/qv4mm_p.h>
#include "qv4arrayobject_p.h"
@@ -57,6 +58,7 @@
#include "private/qlocale_tools_p.h"
#include "private/qqmlbuiltinfunctions_p.h"
#include <private/qv4jscall_p.h>
+#include <private/qv4vme_moth_p.h>
#include <QtCore/QDebug>
#include <algorithm>
@@ -72,31 +74,38 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc))
{
jsCall = code;
- jsConstruct = QV4::FunctionObject::callAsConstructor;
+ jsConstruct = QV4::FunctionObject::virtualCallAsConstructor;
Object::init();
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
- f->init(name, false);
+ if (name)
+ f->setName(name);
}
void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto)
{
- jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
- jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+ jsCall = vtable()->call;
+ jsConstruct = vtable()->callAsConstructor;
Object::init();
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
- f->init(name, createProto);
+ if (name)
+ f->setName(name);
+
+ if (createProto)
+ f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor);
}
+
+
void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto)
{
- jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
- jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+ jsCall = vtable()->call;
+ jsConstruct = vtable()->callAsConstructor;
Object::init();
setFunction(function);
@@ -104,7 +113,11 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function
Scope s(scope->engine());
ScopedString name(s, function->name());
ScopedFunctionObject f(s, this);
- f->init(name, createProto);
+ if (name)
+ f->setName(name);
+
+ if (createProto)
+ f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor);
}
void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name, bool createProto)
@@ -116,12 +129,12 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &nam
void Heap::FunctionObject::init()
{
- jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
- jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+ jsCall = vtable()->call;
+ jsConstruct = vtable()->callAsConstructor;
Object::init();
this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d());
- Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype);
+ Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()->propertyKey()) == Index_Prototype);
setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue());
}
@@ -139,23 +152,16 @@ void Heap::FunctionObject::destroy()
Object::destroy();
}
-void FunctionObject::init(String *n, bool createProto)
+void FunctionObject::createDefaultPrototypeProperty(uint protoSlot, uint protoConstructorSlot)
{
- Scope s(internalClass()->engine);
- ScopedValue protectThis(s, this);
+ Scope s(this);
- Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype);
- if (createProto) {
- ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype()));
- Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor);
- proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d());
- setProperty(Heap::FunctionObject::Index_Prototype, proto);
- } else {
- setProperty(Heap::FunctionObject::Index_Prototype, Primitive::undefinedValue());
- }
+ Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()->propertyKey()) == protoSlot);
+ Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->find(s.engine->id_constructor()->propertyKey()) == protoConstructorSlot);
- if (n)
- defineReadonlyConfigurableProperty(s.engine->id_name(), *n);
+ ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses(EngineBase::Class_ObjectProto)));
+ proto->setProperty(protoConstructorSlot, d());
+ setProperty(protoSlot, proto);
}
ReturnedValue FunctionObject::name() const
@@ -163,19 +169,48 @@ ReturnedValue FunctionObject::name() const
return get(scope()->internalClass->engine->id_name());
}
-ReturnedValue FunctionObject::callAsConstructor(const FunctionObject *f, const Value *, int)
+ReturnedValue FunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
{
return f->engine()->throwTypeError();
}
-ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Value *, int)
+ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int)
{
return Encode::undefined();
}
Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function)
{
- return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function);
+ return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function);
+}
+
+Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor)
+{
+ if (!function) {
+ Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope);
+ c->isDerivedConstructor = isDerivedConstructor;
+ return c;
+ }
+ Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function);
+ c->isDerivedConstructor = isDerivedConstructor;
+ return c;
+}
+
+Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function)
+{
+ return scope->engine()->memoryManager->allocate<MemberFunction>(scope, function);
+}
+
+Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount)
+{
+ Scope scope(engine);
+ ScopedString name(scope, nameOrSymbol);
+ if (!name)
+ name = engine->newString(QChar::fromLatin1('[') + nameOrSymbol->toQString().midRef(1) + QChar::fromLatin1(']'));
+
+ ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code));
+ function->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(argumentCount));
+ return function->d();
}
bool FunctionObject::isBinding() const
@@ -201,10 +236,8 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope)
}
// 15.3.2
-ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t)
{
- Scope scope(f->engine());
-
QString arguments;
QString body;
if (argc > 0) {
@@ -215,42 +248,58 @@ ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Val
}
body = argv[argc - 1].toQString();
}
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (engine->hasException)
+ return nullptr;
- QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}');
+ QString function = (t == Type_Function ? QLatin1String("function anonymous(") : QLatin1String("function* anonymous(")) + arguments + QLatin1String("\n){") + body + QLatin1String("\n}");
- QQmlJS::Engine ee, *engine = &ee;
- QQmlJS::Lexer lexer(engine);
+ QQmlJS::Engine ee;
+ QQmlJS::Lexer lexer(&ee);
lexer.setCode(function, 1, false);
- QQmlJS::Parser parser(engine);
+ QQmlJS::Parser parser(&ee);
const bool parsed = parser.parseExpression();
- if (!parsed)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!parsed) {
+ engine->throwSyntaxError(QLatin1String("Parse error"));
+ return nullptr;
+ }
QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode());
- if (!fe)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!fe) {
+ engine->throwSyntaxError(QLatin1String("Parse error"));
+ return nullptr;
+ }
- Compiler::Module module(scope.engine->debugger() != nullptr);
+ Compiler::Module module(engine->debugger() != nullptr);
Compiler::JSUnitGenerator jsGenerator(&module);
- RuntimeCodegen cg(scope.engine, &jsGenerator, false);
+ RuntimeCodegen cg(engine, &jsGenerator, false);
cg.generateFromFunctionExpression(QString(), function, fe, &module);
- QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit();
- Function *vmf = compilationUnit->linkToEngine(scope.engine);
+ if (engine->hasException)
+ return nullptr;
- ExecutionContext *global = scope.engine->rootContext();
+ return cg.generateCompilationUnit();
+}
+
+ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ ExecutionEngine *engine = f->engine();
+
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ Function *vmf = compilationUnit->linkToEngine(engine);
+ ExecutionContext *global = engine->scriptContext();
return Encode(FunctionObject::createScriptFunction(global, vmf));
}
// 15.3.1: This is equivalent to new Function(...)
-ReturnedValue FunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue FunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
DEFINE_OBJECT_VTABLE(FunctionPrototype);
@@ -268,13 +317,14 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor)
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ defineReadonlyConfigurableProperty(engine->id_name(), *engine->id_empty());
defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
defineDefaultProperty(QStringLiteral("apply"), method_apply, 2);
defineDefaultProperty(QStringLiteral("call"), method_call, 1);
defineDefaultProperty(QStringLiteral("bind"), method_bind, 1);
-
+ defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly);
}
ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -284,7 +334,19 @@ ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const
if (!fun)
return v4->throwTypeError();
- return Encode(v4->newString(QStringLiteral("function() { [code] }")));
+ const Scope scope(fun->engine());
+ const ScopedString scopedFunctionName(scope, fun->name());
+ const QString functionName(scopedFunctionName ? scopedFunctionName->toQString() : QString());
+ QString functionAsString = QStringLiteral("function");
+
+ // If fun->name() is empty, then there is no function name
+ // to append because the function is anonymous.
+ if (!functionName.isEmpty())
+ functionAsString.append(QLatin1Char(' ') + functionName);
+
+ functionAsString.append(QStringLiteral("() { [code] }"));
+
+ return Encode(v4->newString(functionAsString));
}
ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -304,7 +366,7 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
uint len = arr->getLength();
Scope scope(v4);
- Value *arguments = v4->jsAlloca(len);
+ Value *arguments = scope.alloc(len);
if (len) {
if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) {
QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>();
@@ -323,7 +385,7 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
arguments[i] = Primitive::undefinedValue();
} else {
for (quint32 i = 0; i < len; ++i)
- arguments[i] = arr->getIndexed(i);
+ arguments[i] = arr->get(i);
}
}
@@ -383,18 +445,50 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
return bound->asReturnedValue();
}
+ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc)
+ return false;
+ const Object *o = thisObject->as<Object>();
+ if (!o)
+ return f->engine()->throwTypeError();
+
+ return Object::virtualInstanceOf(o, argv[0]);
+}
+
DEFINE_OBJECT_VTABLE(ScriptFunction);
-ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
+ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget)
{
ExecutionEngine *v4 = fo->engine();
const ScriptFunction *f = static_cast<const ScriptFunction *>(fo);
+ Q_ASSERT(newTarget->isFunctionObject());
+ const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
Scope scope(v4);
- InternalClass *ic = f->classForConstructor();
+ Scoped<InternalClass> ic(scope);
+ if (nt->d() == f->d()) {
+ ic = f->classForConstructor();
+ } else {
+ const Object *o = nt->d()->protoProperty();
+ ic = scope.engine->internalClasses(EngineBase::Class_Object);
+ if (o)
+ ic = ic->changePrototype(o->d());
+ }
ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic));
- ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc);
+ CppStackFrame frame;
+ frame.init(v4, f->function(), argv, argc);
+ frame.setupJSFrame(v4->jsStackTop, *f, f->scope(),
+ thisObject,
+ newTarget ? *newTarget : Primitive::undefinedValue());
+
+ frame.push();
+ v4->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result = Moth::VME::exec(&frame, v4);
+
+ frame.pop();
if (Q_UNLIKELY(v4->hasException))
return Encode::undefined();
@@ -403,9 +497,23 @@ ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const
return result;
}
-ReturnedValue ScriptFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue ScriptFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
{
- return Moth::VME::exec(fo, thisObject, argv, argc);
+ ExecutionEngine *engine = fo->engine();
+ CppStackFrame frame;
+ frame.init(engine, fo->function(), argv, argc);
+ frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(),
+ thisObject ? *thisObject : Primitive::undefinedValue(),
+ Primitive::undefinedValue());
+
+ frame.push();
+ engine->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result = Moth::VME::exec(&frame, engine);
+
+ frame.pop();
+
+ return result;
}
void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function)
@@ -415,38 +523,123 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
setFunction(function);
Q_ASSERT(function);
- Q_ASSERT(function->code);
Scope s(scope);
ScopedFunctionObject f(s, this);
ScopedString name(s, function->name());
- f->init(name, true);
- Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length);
- setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount()));
-
- if (function->isStrict()) {
- ScopedProperty pd(s);
- pd->value = s.engine->thrower();
- pd->set = s.engine->thrower();
- f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- }
+ if (name)
+ f->setName(name);
+ f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_Prototype, Heap::FunctionObject::Index_ProtoConstructor);
+
+ Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()->propertyKey()) == Index_Length);
+ setProperty(s.engine, Index_Length, Primitive::fromInt32(int(function->compiledFunction->length)));
}
-InternalClass *ScriptFunction::classForConstructor() const
+Heap::InternalClass *ScriptFunction::classForConstructor() const
{
const Object *o = d()->protoProperty();
- InternalClass *ic = d()->cachedClassForConstructor;
- if (ic && ic->prototype == o->d())
- return ic;
+ if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->d())
+ return d()->cachedClassForConstructor;
- ic = engine()->internalClasses[EngineBase::Class_Object];
+ Scope scope(engine());
+ Scoped<InternalClass> ic(scope, engine()->internalClasses(EngineBase::Class_Object));
if (o)
ic = ic->changePrototype(o->d());
- d()->cachedClassForConstructor = ic;
+ d()->cachedClassForConstructor.set(scope.engine, ic->d());
+
+ return ic->d();
+}
+
+DEFINE_OBJECT_VTABLE(ConstructorFunction);
+
+ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ const ConstructorFunction *c = static_cast<const ConstructorFunction *>(f);
+ if (!c->d()->isDerivedConstructor)
+ return ScriptFunction::virtualCallAsConstructor(f, argv, argc, newTarget);
+
+ ExecutionEngine *v4 = f->engine();
+
+ CppStackFrame frame;
+ frame.init(v4, f->function(), argv, argc);
+ frame.setupJSFrame(v4->jsStackTop, *f, f->scope(),
+ Primitive::undefinedValue(),
+ newTarget ? *newTarget : Primitive::undefinedValue());
+
+ frame.push();
+ v4->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result = Moth::VME::exec(&frame, v4);
+
+ frame.pop();
+
+ if (Q_UNLIKELY(v4->hasException))
+ return Encode::undefined();
+ else if (Value::fromReturnedValue(result).isObject())
+ return result;
+ else if (!Value::fromReturnedValue(result).isUndefined())
+ return v4->throwTypeError();
+ return frame.jsFrame->thisObject.asReturnedValue();
+}
+
+ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|"));
+}
- return ic;
+DEFINE_OBJECT_VTABLE(MemberFunction);
+
+ReturnedValue MemberFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
+{
+ return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor."));
+}
+
+DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction);
+
+ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ const DefaultClassConstructorFunction *c = static_cast<const DefaultClassConstructorFunction *>(f);
+ ExecutionEngine *v4 = f->engine();
+
+ Scope scope(v4);
+
+ if (!c->d()->isDerivedConstructor) {
+ ScopedObject proto(scope, static_cast<const Object *>(newTarget) ->get(scope.engine->id_prototype()));
+ ScopedObject c(scope, scope.engine->newObject());
+ c->setPrototypeUnchecked(proto);
+ return c->asReturnedValue();
+ }
+
+ ScopedFunctionObject super(scope, f->getPrototypeOf());
+ Q_ASSERT(super->isFunctionObject());
+
+ CppStackFrame frame;
+ frame.init(v4, nullptr, argv, argc);
+ frame.setupJSFrame(v4->jsStackTop, *f, f->scope(),
+ Primitive::undefinedValue(),
+ newTarget ? *newTarget : Primitive::undefinedValue(), argc, argc);
+
+ frame.push();
+ v4->jsStackTop += frame.requiredJSStackFrameSize(argc);
+
+ // Do a super call
+ ReturnedValue result = super->callAsConstructor(argv, argc, newTarget);
+
+ frame.pop();
+
+ if (Q_UNLIKELY(v4->hasException))
+ return Encode::undefined();
+ else if (Value::fromReturnedValue(result).isObject())
+ return result;
+ else if (!Value::fromReturnedValue(result).isUndefined())
+ return v4->throwTypeError();
+ return frame.jsFrame->thisObject.asReturnedValue();
+}
+
+ReturnedValue DefaultClassConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|"));
}
DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction);
@@ -479,7 +672,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject
f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
}
-ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const Value *argv, int argc)
+ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc)
{
const BoundFunction *f = static_cast<const BoundFunction *>(fo);
Scope scope(f->engine());
@@ -500,7 +693,7 @@ ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const
return target->call(jsCallData);
}
-ReturnedValue BoundFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
+ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *)
{
const BoundFunction *f = static_cast<const BoundFunction *>(fo);
Scope scope(f->engine());
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index 32e71a175b..1acb1df0b4 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -64,17 +64,14 @@ namespace QV4 {
struct IndexedBuiltinFunction;
struct JSCallData;
-typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
-typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc);
-
namespace Heap {
#define FunctionObjectMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, scope) \
Member(class, NoMark, Function *, function) \
- Member(class, NoMark, jsCallFunction, jsCall) \
- Member(class, NoMark, jsConstructFunction, jsConstruct)
+ Member(class, NoMark, VTable::Call, jsCall) \
+ Member(class, NoMark, VTable::CallAsConstructor, jsConstruct)
DECLARE_HEAP_OBJECT(FunctionObject, Object) {
DECLARE_MARKOBJECTS(FunctionObject);
@@ -111,14 +108,30 @@ struct IndexedBuiltinFunction : FunctionObject {
uint index;
};
-struct ScriptFunction : FunctionObject {
+#define ScriptFunctionMembers(class, Member) \
+ Member(class, Pointer, InternalClass *, cachedClassForConstructor)
+
+DECLARE_HEAP_OBJECT(ScriptFunction, FunctionObject) {
+ DECLARE_MARKOBJECTS(ScriptFunction)
enum {
Index_Name = FunctionObject::Index_Prototype + 1,
Index_Length
};
void init(QV4::ExecutionContext *scope, Function *function);
+};
- QV4::InternalClass *cachedClassForConstructor;
+struct ConstructorFunction : ScriptFunction
+{
+ bool isDerivedConstructor;
+};
+
+struct MemberFunction : ScriptFunction
+{
+};
+
+struct DefaultClassConstructorFunction : FunctionObject
+{
+ bool isDerivedConstructor;
};
#define BoundFunctionMembers(class, Member) \
@@ -152,25 +165,26 @@ struct Q_QML_EXPORT FunctionObject: Object {
unsigned int formalParameterCount() const { return d()->formalParameterCount(); }
unsigned int varCount() const { return d()->varCount(); }
- void init(String *name, bool createProto);
+ void setName(String *name) {
+ defineReadonlyConfigurableProperty(engine()->id_name(), *name);
+ }
+ void createDefaultPrototypeProperty(uint protoSlot, uint protoConstructorSlot);
inline ReturnedValue callAsConstructor(const JSCallData &data) const;
- ReturnedValue callAsConstructor(const Value *argv, int argc) const {
- return d()->jsConstruct(this, argv, argc);
+ ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const {
+ return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this);
}
inline ReturnedValue call(const JSCallData &data) const;
ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const {
return d()->jsCall(this, thisObject, argv, argc);
}
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function);
- static Heap::FunctionObject *createBuiltinFunction(ExecutionContext *scope, String *name,
- ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc))
- {
- return scope->engine()->memoryManager->allocObject<FunctionObject>(scope, name, code);
- }
+ static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, bool isDerivedConstructor);
+ static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function);
+ static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount);
bool strictMode() const { return d()->function ? d()->function->isStrict() : false; }
bool isBinding() const;
@@ -181,7 +195,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
template<>
inline const FunctionObject *Value::as() const {
- return isManaged() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr;
}
@@ -189,8 +203,14 @@ struct FunctionCtor: FunctionObject
{
V4_OBJECT2(FunctionCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+protected:
+ enum Type {
+ Type_Function,
+ Type_Generator
+ };
+ static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function);
};
struct FunctionPrototype: FunctionObject
@@ -203,6 +223,7 @@ struct FunctionPrototype: FunctionObject
static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct IndexedBuiltinFunction : FunctionObject
@@ -224,27 +245,46 @@ struct ScriptFunction : FunctionObject {
V4_INTERNALCLASS(ScriptFunction)
enum { NInlineProperties = 3 };
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+
+ Heap::InternalClass *classForConstructor() const;
+};
+
+struct ConstructorFunction : ScriptFunction {
+ V4_OBJECT2(ConstructorFunction, ScriptFunction)
+ V4_INTERNALCLASS(ConstructorFunction)
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
- InternalClass *classForConstructor() const;
+struct MemberFunction : ScriptFunction {
+ V4_OBJECT2(MemberFunction, ScriptFunction)
+ V4_INTERNALCLASS(MemberFunction)
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
};
+struct DefaultClassConstructorFunction : FunctionObject {
+ V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject)
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
struct BoundFunction: FunctionObject {
V4_OBJECT2(BoundFunction, FunctionObject)
static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs)
{
- return scope->engine()->memoryManager->allocObject<BoundFunction>(scope, target, boundThis, boundArgs);
+ return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs);
}
Heap::FunctionObject *target() const { return d()->target; }
Value boundThis() const { return d()->boundThis; }
Heap::MemberData *boundArgs() const { return d()->boundArgs; }
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
new file mode 100644
index 0000000000..8c3cd8b863
--- /dev/null
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -0,0 +1,252 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qv4generatorobject_p.h>
+#include <qv4symbol_p.h>
+#include <qv4iterator_p.h>
+#include <qv4jscall_p.h>
+#include <qv4vme_moth_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(GeneratorFunctionCtor);
+DEFINE_OBJECT_VTABLE(GeneratorFunction);
+DEFINE_OBJECT_VTABLE(GeneratorObject);
+
+void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction"));
+}
+
+ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ ExecutionEngine *engine = f->engine();
+
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ Function *vmf = compilationUnit->linkToEngine(engine);
+ ExecutionContext *global = engine->scriptContext();
+ return Encode(GeneratorFunction::create(global, vmf));
+}
+
+// 15.3.1: This is equivalent to new Function(...)
+ReturnedValue GeneratorFunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return virtualCallAsConstructor(f, argv, argc, f);
+}
+
+Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Function *function)
+{
+ Scope scope(context);
+ Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function));
+ ScopedObject proto(scope, scope.engine->newObject());
+ proto->setPrototypeOf(scope.engine->generatorPrototype());
+ g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
+ g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ return g->d();
+}
+
+ReturnedValue GeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f);
+ Function *function = gf->function();
+ ExecutionEngine *engine = gf->engine();
+
+ // We need to set up a separate stack for the generator, as it's being re-entered
+ uint stackSize = argc // space for the original arguments
+ + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame
+
+ size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize;
+
+ Scope scope(gf);
+ Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject]));
+ g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
+
+ Heap::GeneratorObject *gp = g->d();
+ gp->stack.size = stackSize;
+ gp->stack.alloc = stackSize;
+
+ // copy original arguments
+ memcpy(gp->stack.values, argv, argc*sizeof(Value));
+ gp->cppFrame.init(engine, function, gp->stack.values, argc);
+ gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(),
+ thisObject ? *thisObject : Primitive::undefinedValue(),
+ Primitive::undefinedValue());
+
+ gp->cppFrame.push();
+
+ Moth::VME::interpret(&gp->cppFrame, engine, function->codeData);
+ gp->state = GeneratorState::SuspendedStart;
+
+ gp->cppFrame.pop();
+ return g->asReturnedValue();
+}
+
+
+void Heap::GeneratorPrototype::init()
+{
+ Heap::FunctionObject::init();
+}
+
+
+void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedValue v(scope);
+
+ ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype())));
+
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto);
+
+ ctorProto->defineDefaultProperty(QStringLiteral("constructor"), (v = ctor), Attr_ReadOnly_ButConfigurable);
+ ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable);
+ ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable);
+
+ setPrototypeOf(engine->iteratorPrototype());
+ defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable);
+ defineDefaultProperty(QStringLiteral("next"), method_next, 1);
+ defineDefaultProperty(QStringLiteral("return"), method_return, 1);
+ defineDefaultProperty(QStringLiteral("throw"), method_throw, 1);
+ defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newString(QStringLiteral("Generator"))), Attr_ReadOnly_ButConfigurable);
+}
+
+ReturnedValue GeneratorPrototype::method_next(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+ Heap::GeneratorObject *gp = g->d();
+
+ if (gp->state == GeneratorState::Completed)
+ return IteratorPrototype::createIterResultObject(engine, Primitive::undefinedValue(), true);
+
+ return g->resume(engine, argc ? argv[0] : Primitive::undefinedValue());
+}
+
+ReturnedValue GeneratorPrototype::method_return(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+
+ Heap::GeneratorObject *gp = g->d();
+
+ if (gp->state == GeneratorState::SuspendedStart)
+ gp->state = GeneratorState::Completed;
+
+ if (gp->state == GeneratorState::Completed)
+ return IteratorPrototype::createIterResultObject(engine, argc ? argv[0] : Primitive::undefinedValue(), true);
+
+ // the bytecode interpreter interprets an exception with empty value as
+ // a yield called with return()
+ engine->throwError(Primitive::emptyValue());
+
+ return g->resume(engine, argc ? argv[0]: Primitive::undefinedValue());
+}
+
+ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+
+ Heap::GeneratorObject *gp = g->d();
+
+ engine->throwError(argc ? argv[0]: Primitive::undefinedValue());
+
+ if (gp->state == GeneratorState::SuspendedStart || gp->state == GeneratorState::Completed) {
+ gp->state = GeneratorState::Completed;
+ return Encode::undefined();
+ }
+
+ return g->resume(engine, Primitive::undefinedValue());
+}
+
+ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) const
+{
+ Heap::GeneratorObject *gp = d();
+ gp->state = GeneratorState::Executing;
+ gp->cppFrame.parent = engine->currentStackFrame;
+ engine->currentStackFrame = &gp->cppFrame;
+
+ Q_ASSERT(gp->cppFrame.yield != nullptr);
+ const char *code = gp->cppFrame.yield;
+ gp->cppFrame.yield = nullptr;
+ gp->cppFrame.jsFrame->accumulator = arg;
+
+ Scope scope(engine);
+ ScopedValue result(scope, Moth::VME::interpret(&gp->cppFrame, engine, code));
+
+ engine->currentStackFrame = gp->cppFrame.parent;
+
+ bool done = (gp->cppFrame.yield == nullptr);
+ gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield;
+ if (engine->hasException)
+ return Encode::undefined();
+ return IteratorPrototype::createIterResultObject(engine, result, done);
+}
+
+DEFINE_OBJECT_VTABLE(MemberGeneratorFunction);
+
+Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, Function *function)
+{
+ Scope scope(context);
+ Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<MemberGeneratorFunction>(context, function));
+ ScopedObject proto(scope, scope.engine->newObject());
+ proto->setPrototypeOf(scope.engine->generatorPrototype());
+ g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
+ g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ return g->d();
+}
+
+ReturnedValue MemberGeneratorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
+{
+ return f->engine()->throwTypeError(QStringLiteral("Function is not a constructor."));
+}
diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h
new file mode 100644
index 0000000000..a97050473c
--- /dev/null
+++ b/src/qml/jsruntime/qv4generatorobject_p.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4GENERATOROBJECT_P_H
+#define QV4GENERATOROBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4functionobject_p.h"
+#include "qv4stackframe_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+enum class GeneratorState {
+ Undefined,
+ SuspendedStart,
+ SuspendedYield,
+ Executing,
+ Completed
+};
+
+namespace Heap {
+
+struct GeneratorFunctionCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct GeneratorFunction : ScriptFunction {
+};
+
+struct MemberGeneratorFunction : ScriptFunction {
+};
+
+struct GeneratorPrototype : FunctionObject {
+ void init();
+};
+
+#define GeneratorObjectMembers(class, Member) \
+ Member(class, Pointer, ExecutionContext *, context) \
+ Member(class, Pointer, GeneratorFunction *, function) \
+ Member(class, NoMark, GeneratorState, state) \
+ Member(class, NoMark, CppStackFrame, cppFrame) \
+ Member(class, ValueArray, ValueArray, stack)
+
+DECLARE_HEAP_OBJECT(GeneratorObject, Object) {
+ DECLARE_MARKOBJECTS(GeneratorObject);
+};
+
+}
+
+struct GeneratorFunctionCtor : FunctionCtor
+{
+ V4_OBJECT2(GeneratorFunctionCtor, FunctionCtor)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct GeneratorFunction : ScriptFunction
+{
+ V4_OBJECT2(GeneratorFunction, ScriptFunction)
+ V4_INTERNALCLASS(GeneratorFunction)
+
+ static Heap::FunctionObject *create(ExecutionContext *scope, Function *function);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MemberGeneratorFunction : GeneratorFunction
+{
+ V4_OBJECT2(MemberGeneratorFunction, GeneratorFunction)
+ V4_INTERNALCLASS(MemberGeneratorFunction)
+
+ static Heap::FunctionObject *create(ExecutionContext *scope, Function *function);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+};
+
+struct GeneratorPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_next(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_return(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_throw(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+struct GeneratorObject : Object {
+ V4_OBJECT2(GeneratorObject, Object)
+ Q_MANAGED_TYPE(GeneratorObject)
+ V4_INTERNALCLASS(GeneratorObject)
+ V4_PROTOTYPE(generatorPrototype)
+
+ ReturnedValue resume(ExecutionEngine *engine, const Value &arg) const;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4GENERATORFUNCTION_P_H
+
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 1fa4bae049..e3b3423a9d 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -153,6 +153,11 @@ namespace Compiler {
struct Module;
struct Context;
struct JSUnitGenerator;
+ class Codegen;
+}
+
+namespace Moth {
+ class BytecodeGenerator;
}
namespace Heap {
@@ -160,13 +165,17 @@ namespace Heap {
struct MemberData;
struct ArrayData;
+ struct StringOrSymbol;
struct String;
+ struct Symbol;
struct Object;
struct ObjectPrototype;
struct ExecutionContext;
struct CallContext;
+ struct QmlContext;
struct ScriptFunction;
+ struct InternalClass;
struct BooleanObject;
struct NumberObject;
@@ -188,14 +197,19 @@ namespace Heap {
template <typename T, size_t> struct Pointer;
}
+struct CppStackFrame;
class MemoryManager;
class ExecutableAllocator;
+struct PropertyKey;
+struct StringOrSymbol;
struct String;
+struct Symbol;
struct Object;
struct ObjectPrototype;
struct ObjectIterator;
struct ExecutionContext;
struct CallContext;
+struct QmlContext;
struct ScriptFunction;
struct InternalClass;
struct Property;
@@ -236,6 +250,7 @@ struct Scope;
struct ScopedValue;
template<typename T> struct Scoped;
typedef Scoped<String> ScopedString;
+typedef Scoped<StringOrSymbol> ScopedStringOrSymbol;
typedef Scoped<Object> ScopedObject;
typedef Scoped<ArrayObject> ScopedArrayObject;
typedef Scoped<FunctionObject> ScopedFunctionObject;
@@ -244,6 +259,7 @@ typedef Scoped<ExecutionContext> ScopedContext;
struct PersistentValueStorage;
class PersistentValue;
class WeakValue;
+struct MarkStack;
struct IdentifierTable;
class RegExpCache;
@@ -357,6 +373,12 @@ struct Q_QML_EXPORT StackFrame {
};
typedef QVector<StackFrame> StackTrace;
+enum class ObjectLiteralArgument {
+ Value,
+ Getter,
+ Setter
+};
+
}
Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE);
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index f419ab53fe..43895011f3 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -335,7 +335,7 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope)
Scope s(scope);
Heap::FunctionObject::init(scope, s.engine->id_eval());
ScopedFunctionObject f(s, this);
- f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1));
+ f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(1));
}
ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const
@@ -351,7 +351,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
if (!directCall) {
// the context for eval should be the global scope
- ctx = v4->rootContext();
+ ctx = v4->scriptContext();
}
String *scode = argv[0].stringValue();
@@ -361,7 +361,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
const QString code = scode->toQString();
bool inheritContext = !isStrict;
- Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code"));
+ Script script(ctx, QV4::Compiler::ContextType::Eval, code, QStringLiteral("eval code"));
script.strictMode = (directCall && isStrict);
script.inheritContext = inheritContext;
script.parse();
@@ -384,7 +384,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
}
-ReturnedValue EvalFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue EvalFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
// indirect call
return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false);
diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h
index fd1820c23c..021b445955 100644
--- a/src/qml/jsruntime/qv4globalobject_p.h
+++ b/src/qml/jsruntime/qv4globalobject_p.h
@@ -71,7 +71,7 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject
ReturnedValue evalCall(const Value *thisObject, const Value *argv, int argc, bool directCall) const;
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct GlobalFunctions
diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp
index c122bcb51a..5db5bd46ec 100644
--- a/src/qml/jsruntime/qv4identifier.cpp
+++ b/src/qml/jsruntime/qv4identifier.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qv4identifier_p.h"
#include "qv4identifiertable_p.h"
+#include "qv4string_p.h"
QT_BEGIN_NAMESPACE
@@ -54,14 +55,16 @@ static inline int primeForNumBits(int numBits)
}
-IdentifierHashData::IdentifierHashData(int numBits)
+IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits)
: size(0)
, numBits(numBits)
+ , identifierTable(table)
{
refCount.store(1);
alloc = primeForNumBits(numBits);
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memset(entries, 0, alloc*sizeof(IdentifierHashEntry));
+ identifierTable->addIdentifierHash(this);
}
IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
@@ -73,12 +76,18 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
alloc = other->alloc;
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry));
+ identifierTable->addIdentifierHash(this);
+}
+
+IdentifierHashData::~IdentifierHashData() {
+ free(entries);
+ if (identifierTable)
+ identifierTable->removeIdentifierHash(this);
}
IdentifierHash::IdentifierHash(ExecutionEngine *engine)
{
- d = new IdentifierHashData(3);
- d->identifierTable = engine->identifierTable;
+ d = new IdentifierHashData(engine->identifierTable, 3);
}
void IdentifierHash::detach()
@@ -92,8 +101,10 @@ void IdentifierHash::detach()
}
-IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
+IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier)
{
+ Q_ASSERT(identifier.isStringOrSymbol());
+
// fill up to max 50%
bool grow = (d->alloc <= d->size*2);
@@ -104,10 +115,10 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry));
for (int i = 0; i < d->alloc; ++i) {
const IdentifierHashEntry &e = d->entries[i];
- if (!e.identifier)
+ if (!e.identifier.isValid())
continue;
- uint idx = e.identifier->hashValue % newAlloc;
- while (newEntries[idx].identifier) {
+ uint idx = e.identifier.id() % newAlloc;
+ while (newEntries[idx].identifier.isValid()) {
++idx;
idx %= newAlloc;
}
@@ -118,8 +129,8 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
d->alloc = newAlloc;
}
- uint idx = identifier->hashValue % d->alloc;
- while (d->entries[idx].identifier) {
+ uint idx = identifier.id() % d->alloc;
+ while (d->entries[idx].identifier.isValid()) {
Q_ASSERT(d->entries[idx].identifier != identifier);
++idx;
idx %= d->alloc;
@@ -129,15 +140,15 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
return d->entries + idx;
}
-const IdentifierHashEntry *IdentifierHash::lookup(const Identifier *identifier) const
+const IdentifierHashEntry *IdentifierHash::lookup(PropertyKey identifier) const
{
- if (!d)
+ if (!d || !identifier.isStringOrSymbol())
return nullptr;
Q_ASSERT(d->entries);
- uint idx = identifier->hashValue % d->alloc;
+ uint idx = identifier.id() % d->alloc;
while (1) {
- if (!d->entries[idx].identifier)
+ if (!d->entries[idx].identifier.isValid())
return nullptr;
if (d->entries[idx].identifier == identifier)
return d->entries + idx;
@@ -150,39 +161,54 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const
{
if (!d)
return nullptr;
- Q_ASSERT(d->entries);
- uint hash = String::createHashValue(str.constData(), str.length(), nullptr);
- uint idx = hash % d->alloc;
- while (1) {
- if (!d->entries[idx].identifier)
- return nullptr;
- if (d->entries[idx].identifier->string == str)
- return d->entries + idx;
- ++idx;
- idx %= d->alloc;
- }
+ PropertyKey id = d->identifierTable->asPropertyKey(str);
+ return lookup(id);
}
const IdentifierHashEntry *IdentifierHash::lookup(String *str) const
{
if (!d)
return nullptr;
- if (str->d()->identifier)
- return lookup(str->d()->identifier);
+ PropertyKey id = d->identifierTable->asPropertyKey(str);
+ if (id.isValid())
+ return lookup(id);
return lookup(str->toQString());
}
-const Identifier *IdentifierHash::toIdentifier(const QString &str) const
+const PropertyKey IdentifierHash::toIdentifier(const QString &str) const
{
Q_ASSERT(d);
- return d->identifierTable->identifier(str);
+ return d->identifierTable->asPropertyKey(str);
}
-const Identifier *IdentifierHash::toIdentifier(Heap::String *str) const
+const PropertyKey IdentifierHash::toIdentifier(Heap::String *str) const
{
Q_ASSERT(d);
- return d->identifierTable->identifier(str);
+ return d->identifierTable->asPropertyKey(str);
+}
+
+QString QV4::IdentifierHash::findId(int value) const
+{
+ IdentifierHashEntry *e = d->entries;
+ IdentifierHashEntry *end = e + d->alloc;
+ while (e < end) {
+ if (e->identifier.isValid() && e->value == value)
+ return e->identifier.toQString();
+ ++e;
+ }
+ return QString();
+}
+
+void IdentifierHashData::markObjects(MarkStack *markStack) const
+{
+ IdentifierHashEntry *e = entries;
+ IdentifierHashEntry *end = e + alloc;
+ while (e < end) {
+ if (Heap::Base *o = e->identifier.asStringOrSymbol())
+ o->mark(markStack);
+ ++e;
+ }
}
diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h
index 82346d5f68..32de8b7c8d 100644
--- a/src/qml/jsruntime/qv4identifier_p.h
+++ b/src/qml/jsruntime/qv4identifier_p.h
@@ -51,38 +51,24 @@
//
#include <qstring.h>
+#include <private/qv4global_p.h>
+#include <private/qv4propertykey_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-namespace Heap {
- struct String;
-}
-
-struct String;
-struct IdentifierTable;
-struct ExecutionEngine;
-
-struct Identifier
-{
- QString string;
- uint hashValue;
-};
-
-
struct IdentifierHashEntry {
- const Identifier *identifier;
+ PropertyKey identifier;
int value;
};
struct IdentifierHashData
{
- IdentifierHashData(int numBits);
+ IdentifierHashData(IdentifierTable *table, int numBits);
explicit IdentifierHashData(IdentifierHashData *other);
- ~IdentifierHashData() {
- free(entries);
- }
+ ~IdentifierHashData();
+ void markObjects(MarkStack *markStack) const;
QBasicAtomicInt refCount;
int alloc;
@@ -117,12 +103,12 @@ struct IdentifierHash
QString findId(int value) const;
protected:
- IdentifierHashEntry *addEntry(const Identifier *i);
- const IdentifierHashEntry *lookup(const Identifier *identifier) const;
+ IdentifierHashEntry *addEntry(PropertyKey i);
+ const IdentifierHashEntry *lookup(PropertyKey identifier) const;
const IdentifierHashEntry *lookup(const QString &str) const;
const IdentifierHashEntry *lookup(String *str) const;
- const Identifier *toIdentifier(const QString &str) const;
- const Identifier *toIdentifier(Heap::String *str) const;
+ const PropertyKey toIdentifier(const QString &str) const;
+ const PropertyKey toIdentifier(Heap::String *str) const;
};
@@ -180,20 +166,6 @@ inline int IdentifierHash::value(String *str) const
return e ? e->value : -1;
}
-
-inline
-QString IdentifierHash::findId(int value) const
-{
- IdentifierHashEntry *e = d->entries;
- IdentifierHashEntry *end = e + d->alloc;
- while (e < end) {
- if (e->identifier && e->value == value)
- return e->identifier->string;
- ++e;
- }
- return QString();
-}
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index b77f9478d3..0412695404 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
#include "qv4identifiertable_p.h"
+#include "qv4symbol_p.h"
QT_BEGIN_NAMESPACE
@@ -53,44 +54,44 @@ static inline int primeForNumBits(int numBits)
}
-IdentifierTable::IdentifierTable(ExecutionEngine *engine)
+IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits)
: engine(engine)
, size(0)
- , numBits(8)
+ , numBits(numBits)
{
alloc = primeForNumBits(numBits);
- entries = (Heap::String **)malloc(alloc*sizeof(Heap::String *));
- memset(entries, 0, alloc*sizeof(Heap::String *));
+ entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
+ entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
+ memset(entriesByHash, 0, alloc*sizeof(Heap::String *));
+ memset(entriesById, 0, alloc*sizeof(Heap::String *));
}
IdentifierTable::~IdentifierTable()
{
- for (int i = 0; i < alloc; ++i)
- if (entries[i])
- delete entries[i]->identifier;
- free(entries);
+ free(entriesByHash);
+ free(entriesById);
+ for (auto &h : idHashes)
+ h->identifierTable = nullptr;
}
-void IdentifierTable::addEntry(Heap::String *str)
+void IdentifierTable::addEntry(Heap::StringOrSymbol *str)
{
uint hash = str->hashValue();
if (str->subtype == Heap::String::StringType_ArrayIndex)
return;
- str->identifier = new Identifier;
- str->identifier->string = str->toQString();
- str->identifier->hashValue = hash;
+ str->identifier = PropertyKey::fromStringOrSymbol(str);
bool grow = (alloc <= size*2);
if (grow) {
++numBits;
int newAlloc = primeForNumBits(numBits);
- Heap::String **newEntries = (Heap::String **)malloc(newAlloc*sizeof(Heap::String *));
- memset(newEntries, 0, newAlloc*sizeof(Heap::String *));
+ Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *));
+ memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *));
for (int i = 0; i < alloc; ++i) {
- Heap::String *e = entries[i];
+ Heap::StringOrSymbol *e = entriesByHash[i];
if (!e)
continue;
uint idx = e->stringHash % newAlloc;
@@ -100,17 +101,42 @@ void IdentifierTable::addEntry(Heap::String *str)
}
newEntries[idx] = e;
}
- free(entries);
- entries = newEntries;
+ free(entriesByHash);
+ entriesByHash = newEntries;
+
+ newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *));
+ memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *));
+ for (int i = 0; i < alloc; ++i) {
+ Heap::StringOrSymbol *e = entriesById[i];
+ if (!e)
+ continue;
+ uint idx = e->identifier.id() % newAlloc;
+ while (newEntries[idx]) {
+ ++idx;
+ idx %= newAlloc;
+ }
+ newEntries[idx] = e;
+ }
+ free(entriesById);
+ entriesById = newEntries;
+
alloc = newAlloc;
}
uint idx = hash % alloc;
- while (entries[idx]) {
+ while (entriesByHash[idx]) {
+ ++idx;
+ idx %= alloc;
+ }
+ entriesByHash[idx] = str;
+
+ idx = str->identifier.id() % alloc;
+ while (entriesById[idx]) {
++idx;
idx %= alloc;
}
- entries[idx] = str;
+ entriesById[idx] = str;
+
++size;
}
@@ -120,10 +146,16 @@ Heap::String *IdentifierTable::insertString(const QString &s)
{
uint subtype;
uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
+ if (subtype == Heap::String::StringType_ArrayIndex) {
+ Heap::String *str = engine->newString(s);
+ str->stringHash = hash;
+ str->subtype = subtype;
+ return str;
+ }
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == s)
- return e;
+ return static_cast<Heap::String *>(e);
++idx;
idx %= alloc;
}
@@ -135,18 +167,42 @@ Heap::String *IdentifierTable::insertString(const QString &s)
return str;
}
+Heap::Symbol *IdentifierTable::insertSymbol(const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+
+ uint subtype;
+ uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
+ uint idx = hash % alloc;
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
+ if (e->stringHash == hash && e->toQString() == s)
+ return static_cast<Heap::Symbol *>(e);
+ ++idx;
+ idx %= alloc;
+ }
+
+ Heap::Symbol *str = Symbol::create(engine, s);
+ str->stringHash = hash;
+ str->subtype = subtype;
+ addEntry(str);
+ return str;
-Identifier *IdentifierTable::identifierImpl(const Heap::String *str)
+}
+
+
+PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str)
{
- if (str->identifier)
+ if (str->identifier.isValid())
return str->identifier;
uint hash = str->hashValue();
- if (str->subtype == Heap::String::StringType_ArrayIndex)
- return nullptr;
+ if (str->subtype == Heap::String::StringType_ArrayIndex) {
+ str->identifier = PropertyKey::fromArrayIndex(hash);
+ return str->identifier;
+ }
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
- if (e->stringHash == hash && e->isEqualTo(str)) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
+ if (e->stringHash == hash && e->toQString() == str->toQString()) {
str->identifier = e->identifier;
return e->identifier;
}
@@ -158,37 +214,117 @@ Identifier *IdentifierTable::identifierImpl(const Heap::String *str)
return str->identifier;
}
-Heap::String *IdentifierTable::stringFromIdentifier(Identifier *i)
+Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey i) const
{
- if (!i)
+ uint arrayIdx = i.asArrayIndex();
+ if (arrayIdx < UINT_MAX)
+ return engine->newString(QString::number(arrayIdx));
+ if (!i.isValid())
return nullptr;
- uint idx = i->hashValue % alloc;
+ uint idx = i.id() % alloc;
while (1) {
- Heap::String *e = entries[idx];
- Q_ASSERT(e);
- if (e->identifier == i)
+ Heap::StringOrSymbol *e = entriesById[idx];
+ if (!e || e->identifier == i)
return e;
++idx;
idx %= alloc;
}
}
-Identifier *IdentifierTable::identifier(const QString &s)
+Heap::String *IdentifierTable::stringForId(PropertyKey i) const
+{
+ Heap::StringOrSymbol *s = resolveId(i);
+ Q_ASSERT(s && s->internalClass->vtable->isString);
+ return static_cast<Heap::String *>(s);
+}
+
+Heap::Symbol *IdentifierTable::symbolForId(PropertyKey i) const
+{
+ Heap::StringOrSymbol *s = resolveId(i);
+ Q_ASSERT(!s || !s->internalClass->vtable->isString);
+ return static_cast<Heap::Symbol *>(s);
+}
+
+void IdentifierTable::markObjects(MarkStack *markStack)
+{
+ for (const auto &h : idHashes)
+ h->markObjects(markStack);
+}
+
+template <typename Key>
+int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::StringOrSymbol *)> f) {
+ int freed = 0;
+ uint lastKey = 0;
+
+ int lastEntry = -1;
+ int start = 0;
+ // start at an empty entry so we compress properly
+ for (; start < alloc; ++start) {
+ if (!table[start])
+ break;
+ }
+
+ for (int i = 0; i < alloc; ++i) {
+ int idx = (i + start) % alloc;
+ Heap::StringOrSymbol *entry = table[idx];
+ if (!entry) {
+ lastEntry = -1;
+ continue;
+ }
+ if (entry->isMarked()) {
+ if (lastEntry >= 0 && lastKey == (f(entry) % alloc)) {
+ Q_ASSERT(table[lastEntry] == nullptr);
+ table[lastEntry] = entry;
+ table[idx] = nullptr;
+
+ // find next free slot just like in addEntry()
+ do {
+ lastEntry = (lastEntry + 1) % alloc;
+ } while (table[lastEntry] != nullptr);
+ }
+ continue;
+ }
+ if (lastEntry == -1) {
+ lastEntry = idx;
+ lastKey = f(entry) % alloc;
+ }
+ table[idx] = nullptr;
+ ++freed;
+ }
+ for (int i = 0; i < alloc; ++i) {
+ Heap::StringOrSymbol *entry = table[i];
+ if (!entry)
+ continue;
+ Q_ASSERT(entry->isMarked());
+ }
+ return freed;
+}
+
+void IdentifierTable::sweep()
+{
+ int f = sweepTable<uint>(entriesByHash, alloc, [](Heap::StringOrSymbol *entry) {return entry->hashValue(); });
+ int freed = sweepTable<quint64>(entriesById, alloc, [](Heap::StringOrSymbol *entry) {return entry->identifier.id(); });
+ Q_UNUSED(f);
+ Q_ASSERT(f == freed);
+ size -= freed;
+}
+
+PropertyKey IdentifierTable::asPropertyKey(const QString &s)
{
return insertString(s)->identifier;
}
-Identifier *IdentifierTable::identifier(const char *s, int len)
+PropertyKey IdentifierTable::asPropertyKey(const char *s, int len)
{
uint subtype;
uint hash = String::createHashValue(s, len, &subtype);
if (hash == UINT_MAX)
- return identifier(QString::fromUtf8(s, len));
+ return asPropertyKey(QString::fromUtf8(s, len));
QLatin1String latin(s, len);
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == latin)
return e->identifier;
++idx;
diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h
index b0b08f1e54..3674ece84f 100644
--- a/src/qml/jsruntime/qv4identifiertable_p.h
+++ b/src/qml/jsruntime/qv4identifiertable_p.h
@@ -53,55 +53,61 @@
#include "qv4identifier_p.h"
#include "qv4string_p.h"
#include "qv4engine_p.h"
+#include <qset.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-struct IdentifierTable
+struct Q_QML_PRIVATE_EXPORT IdentifierTable
{
ExecutionEngine *engine;
int alloc;
int size;
int numBits;
- Heap::String **entries;
+ Heap::StringOrSymbol **entriesByHash;
+ Heap::StringOrSymbol **entriesById;
- void addEntry(Heap::String *str);
+ QSet<IdentifierHashData *> idHashes;
+
+ void addEntry(Heap::StringOrSymbol *str);
public:
- IdentifierTable(ExecutionEngine *engine);
+ IdentifierTable(ExecutionEngine *engine, int numBits = 8);
~IdentifierTable();
Heap::String *insertString(const QString &s);
+ Heap::Symbol *insertSymbol(const QString &s);
- Identifier *identifier(const Heap::String *str) {
- if (str->identifier)
+ PropertyKey asPropertyKey(const Heap::String *str) {
+ if (str->identifier.isValid())
return str->identifier;
- return identifierImpl(str);
+ return asPropertyKeyImpl(str);
}
- Identifier *identifier(const QV4::String *str) {
- return identifier(str->d());
+ PropertyKey asPropertyKey(const QV4::String *str) {
+ return asPropertyKey(str->d());
}
- Identifier *identifier(const QString &s);
- Identifier *identifier(const char *s, int len);
+ PropertyKey asPropertyKey(const QString &s);
+ PropertyKey asPropertyKey(const char *s, int len);
+
+ PropertyKey asPropertyKeyImpl(const Heap::String *str);
- Identifier *identifierImpl(const Heap::String *str);
+ Heap::StringOrSymbol *resolveId(PropertyKey i) const;
+ Heap::String *stringForId(PropertyKey i) const;
+ Heap::Symbol *symbolForId(PropertyKey i) const;
- Heap::String *stringFromIdentifier(Identifier *i);
+ void markObjects(MarkStack *markStack);
+ void sweep();
- void mark(MarkStack *markStack) {
- for (int i = 0; i < alloc; ++i) {
- Heap::String *entry = entries[i];
- if (!entry || entry->isMarked())
- continue;
- entry->setMarkBit();
- Q_ASSERT(entry->vtable()->markObjects);
- entry->vtable()->markObjects(entry, markStack);
- }
+ void addIdentifierHash(IdentifierHashData *h) {
+ idHashes.insert(h);
+ }
+ void removeIdentifierHash(IdentifierHashData *h) {
+ idHashes.remove(h);
}
};
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 3bfcf358bf..c890dc0550 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -47,7 +47,7 @@
QT_BEGIN_NAMESPACE
-using namespace QV4;
+namespace QV4 {
static const uchar prime_deltas[] = {
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
@@ -74,27 +74,11 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
// fill up to max 50%
bool grow = (d->alloc <= d->size*2);
- if (classSize < d->size || grow) {
- PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
- for (int i = 0; i < d->alloc; ++i) {
- const Entry &e = d->entries[i];
- if (!e.identifier || e.index >= static_cast<unsigned>(classSize))
- continue;
- uint idx = e.identifier->hashValue % dd->alloc;
- while (dd->entries[idx].identifier) {
- ++idx;
- idx %= dd->alloc;
- }
- dd->entries[idx] = e;
- }
- dd->size = classSize;
- Q_ASSERT(d->refCount > 1);
- --d->refCount;
- d = dd;
- }
+ if (classSize < d->size || grow)
+ detach(grow, classSize);
- uint idx = entry.identifier->hashValue % d->alloc;
- while (d->entries[idx].identifier) {
+ uint idx = entry.identifier.id() % d->alloc;
+ while (d->entries[idx].identifier.isValid()) {
++idx;
idx %= d->alloc;
}
@@ -102,38 +86,125 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
++d->size;
}
+int PropertyHash::removeIdentifier(PropertyKey identifier, int classSize)
+{
+ int val = -1;
+ PropertyHashData *dd = new PropertyHashData(d->numBits);
+ for (int i = 0; i < d->alloc; ++i) {
+ const Entry &e = d->entries[i];
+ if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
+ continue;
+ if (e.identifier == identifier) {
+ val = e.index;
+ continue;
+ }
+ uint idx = e.identifier.id() % dd->alloc;
+ while (dd->entries[idx].identifier.isValid()) {
+ ++idx;
+ idx %= dd->alloc;
+ }
+ dd->entries[idx] = e;
+ }
+ dd->size = classSize;
+ if (!--d->refCount)
+ delete d;
+ d = dd;
+
+ Q_ASSERT(val != -1);
+ return val;
+}
+
+void PropertyHash::detach(bool grow, int classSize)
+{
+ if (d->refCount == 1 && !grow)
+ return;
-InternalClass::InternalClass(ExecutionEngine *engine)
- : engine(engine)
- , vtable(nullptr)
- , prototype(nullptr)
- , m_sealed(nullptr)
- , m_frozen(nullptr)
- , size(0)
- , extensible(true)
+ PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
+ for (int i = 0; i < d->alloc; ++i) {
+ const Entry &e = d->entries[i];
+ if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
+ continue;
+ uint idx = e.identifier.id() % dd->alloc;
+ while (dd->entries[idx].identifier.isValid()) {
+ ++idx;
+ idx %= dd->alloc;
+ }
+ dd->entries[idx] = e;
+ }
+ dd->size = classSize;
+ if (!--d->refCount)
+ delete d;
+ d = dd;
+}
+
+namespace Heap {
+
+void InternalClass::init(ExecutionEngine *engine)
{
- id = engine->newInternalClassId();
+ Base::init();
+ new (&propertyTable) PropertyHash();
+ new (&nameMap) SharedInternalClassData<PropertyKey>();
+ new (&propertyData) SharedInternalClassData<PropertyAttributes>();
+ new (&transitions) std::vector<Transition>();
+
+ this->engine = engine;
+ vtable = QV4::InternalClass::staticVTable();
+// prototype = nullptr;
+// parent = nullptr;
+// size = 0;
+ extensible = true;
+ isFrozen = false;
+ isSealed = false;
+ isUsedAsProto = false;
+ protoId = engine->newProtoId();
+
+ // Also internal classes need an internal class pointer. Simply make it point to itself
+ internalClass.set(engine, this);
}
-InternalClass::InternalClass(const QV4::InternalClass &other)
- : QQmlJS::Managed()
- , engine(other.engine)
- , vtable(other.vtable)
- , prototype(other.prototype)
- , propertyTable(other.propertyTable)
- , nameMap(other.nameMap)
- , propertyData(other.propertyData)
- , m_sealed(nullptr)
- , m_frozen(nullptr)
- , size(other.size)
- , extensible(other.extensible)
- , isUsedAsProto(other.isUsedAsProto)
+void InternalClass::init(Heap::InternalClass *other)
+{
+ Base::init();
+ Q_ASSERT(!other->isFrozen);
+ new (&propertyTable) PropertyHash(other->propertyTable);
+ new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap);
+ new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
+ new (&transitions) std::vector<Transition>();
+
+ engine = other->engine;
+ vtable = other->vtable;
+ prototype = other->prototype;
+ parent = other;
+ size = other->size;
+ extensible = other->extensible;
+ isSealed = other->isSealed;
+ isFrozen = other->isFrozen;
+ isUsedAsProto = other->isUsedAsProto;
+ protoId = engine->newProtoId();
+
+ internalClass.set(engine, other->internalClass);
+}
+
+void InternalClass::destroy()
{
- id = engine->newInternalClassId();
+#ifndef QT_NO_DEBUG
+ for (const auto &t : transitions) {
+ Q_ASSERT(!t.lookup || !t.lookup->isMarked());
+ }
+#endif
+ if (parent && parent->engine && parent->isMarked())
+ parent->removeChildEntry(this);
+
+ propertyTable.~PropertyHash();
+ nameMap.~SharedInternalClassData<PropertyKey>();
+ propertyData.~SharedInternalClassData<PropertyAttributes>();
+ transitions.~vector<Transition>();
+ engine = nullptr;
+ Base::destroy();
}
-static void insertHoleIntoPropertyData(Object *object, int idx)
+static void insertHoleIntoPropertyData(QV4::Object *object, int idx)
{
Heap::Object *o = object->d();
ExecutionEngine *v4 = o->internalClass->engine;
@@ -142,7 +213,7 @@ static void insertHoleIntoPropertyData(Object *object, int idx)
o->setProperty(v4, i, *o->propertyData(i - 1));
}
-static void removeFromPropertyData(Object *object, int idx, bool accessor = false)
+static void removeFromPropertyData(QV4::Object *object, int idx, bool accessor = false)
{
Heap::Object *o = object->d();
ExecutionEngine *v4 = o->internalClass->engine;
@@ -154,20 +225,23 @@ static void removeFromPropertyData(Object *object, int idx, bool accessor = fals
o->setProperty(v4, size + 1, Primitive::undefinedValue());
}
-void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index)
+void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index)
{
+ Q_ASSERT(id.isStringOrSymbol());
uint idx;
- InternalClass *oldClass = object->internalClass();
- InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx);
+ Heap::InternalClass *oldClass = object->internalClass();
+ Heap::InternalClass *newClass = oldClass->changeMember(id, data, &idx);
if (index)
*index = idx;
+ uint oldSize = oldClass->size;
object->setInternalClass(newClass);
- if (newClass->size > oldClass->size) {
- Q_ASSERT(newClass->size == oldClass->size + 1);
+ // don't use oldClass anymore, it could be GC'ed
+ if (newClass->size > oldSize) {
+ Q_ASSERT(newClass->size == oldSize + 1);
insertHoleIntoPropertyData(object, idx);
- } else if (newClass->size < oldClass->size) {
- Q_ASSERT(newClass->size == oldClass->size - 1);
+ } else if (newClass->size < oldSize) {
+ Q_ASSERT(newClass->size == oldSize - 1);
removeFromPropertyData(object, idx + 1);
}
}
@@ -183,7 +257,16 @@ InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalC
}
}
-InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index)
+static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
+{
+ // add a dummy entry, since we need two entries for accessors
+ newClass->propertyTable.addEntry(e, newClass->size);
+ newClass->nameMap.add(newClass->size, PropertyKey::invalid());
+ newClass->propertyData.add(newClass->size, PropertyAttributes());
+ ++newClass->size;
+}
+
+Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, uint *index)
{
data.resolve();
uint idx = find(identifier);
@@ -193,7 +276,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
*index = idx;
if (data == propertyData.at(idx))
- return this;
+ return static_cast<Heap::InternalClass *>(this);
Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
@@ -201,14 +284,34 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- newClass = newClass->changePrototype(prototype);
- for (uint i = 0; i < size; ++i) {
- if (i == idx) {
- newClass = newClass->addMember(nameMap.at(i), data);
- } else if (!propertyData.at(i).isEmpty()) {
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
+ Heap::InternalClass *newClass = engine->newClass(this);
+ if (data.isAccessor() != propertyData.at(idx).isAccessor()) {
+ // this changes the layout of the class, so we need to rebuild the data
+ newClass->propertyTable = PropertyHash();
+ newClass->nameMap = SharedInternalClassData<PropertyKey>();
+ newClass->propertyData = SharedInternalClassData<PropertyAttributes>();
+ newClass->size = 0;
+ for (uint i = 0; i < size; ++i) {
+ PropertyKey identifier = nameMap.at(i);
+ PropertyHash::Entry e = { identifier, newClass->size };
+ if (i && !identifier.isValid())
+ e.identifier = nameMap.at(i - 1);
+ newClass->propertyTable.addEntry(e, newClass->size);
+ newClass->nameMap.add(newClass->size, identifier);
+ if (i == idx) {
+ newClass->propertyData.add(newClass->size, data);
+ ++newClass->size;
+ if (data.isAccessor())
+ addDummyEntry(newClass, e);
+ else
+ ++i;
+ } else {
+ newClass->propertyData.add(newClass->size, propertyData.at(i));
+ ++newClass->size;
+ }
}
+ } else {
+ newClass->propertyData.set(idx, data);
}
t.lookup = newClass;
@@ -216,14 +319,16 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
return newClass;
}
-InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
+Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
{
+ Scope scope(engine);
+ ScopedValue protectThis(scope, this);
if (proto)
proto->setUsedAsProto();
Q_ASSERT(prototype != proto);
Q_ASSERT(!proto || proto->internalClass->isUsedAsProto);
- Transition temp = { { nullptr }, nullptr, Transition::PrototypeChange };
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange };
temp.prototype = proto;
Transition &t = lookupOrInsertTransition(temp);
@@ -231,29 +336,19 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass;
- if (!size && !prototype) {
- newClass = engine->newClass(*this);
- newClass->prototype = proto;
- } else {
- newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- newClass = newClass->changePrototype(proto);
- for (uint i = 0; i < size; ++i) {
- if (!propertyData.at(i).isEmpty())
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
- }
- }
+ Heap::InternalClass *newClass = engine->newClass(this);
+ newClass->prototype = proto;
t.lookup = newClass;
return newClass;
}
-InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
+Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
{
Q_ASSERT(vtable != vt);
- Transition temp = { { nullptr }, nullptr, Transition::VTableChange };
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::VTableChange };
temp.vtable = vt;
Transition &t = lookupOrInsertTransition(temp);
@@ -261,18 +356,8 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass;
- if (this == engine->internalClasses[EngineBase::Class_Empty]) {
- newClass = engine->newClass(*this);
- newClass->vtable = vt;
- } else {
- newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt);
- newClass = newClass->changePrototype(prototype);
- for (uint i = 0; i < size; ++i) {
- if (!propertyData.at(i).isEmpty())
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
- }
- }
+ Heap::InternalClass *newClass = engine->newClass(this);
+ newClass->vtable = vt;
t.lookup = newClass;
Q_ASSERT(t.lookup);
@@ -280,17 +365,17 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return newClass;
}
-InternalClass *InternalClass::nonExtensible()
+Heap::InternalClass *InternalClass::nonExtensible()
{
if (!extensible)
return this;
- Transition temp = { { nullptr }, nullptr, Transition::NotExtensible};
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible};
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
newClass->extensible = false;
t.lookup = newClass;
@@ -298,31 +383,26 @@ InternalClass *InternalClass::nonExtensible()
return newClass;
}
-void InternalClass::addMember(Object *object, String *string, PropertyAttributes data, uint *index)
+void InternalClass::addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index)
{
+ Q_ASSERT(id.isStringOrSymbol());
data.resolve();
- object->internalClass()->engine->identifierTable->identifier(string);
- if (object->internalClass()->propertyTable.lookup(string->d()->identifier) < object->internalClass()->size) {
- changeMember(object, string, data, index);
+ if (object->internalClass()->propertyTable.lookup(id) < object->internalClass()->size) {
+ changeMember(object, id, data, index);
return;
}
uint idx;
- InternalClass *newClass = object->internalClass()->addMemberImpl(string->identifier(), data, &idx);
+ Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(id, data, &idx);
if (index)
*index = idx;
object->setInternalClass(newClass);
}
-InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index)
-{
- engine->identifierTable->identifier(string);
- return addMember(string->identifier(), data, index);
-}
-
-InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttributes data, uint *index)
+Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAttributes data, uint *index)
{
+ Q_ASSERT(identifier.isStringOrSymbol());
data.resolve();
if (propertyTable.lookup(identifier) < size)
@@ -331,7 +411,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut
return addMemberImpl(identifier, data, index);
}
-InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index)
+Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, uint *index)
{
Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
@@ -343,109 +423,156 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
PropertyHash::Entry e = { identifier, newClass->size };
newClass->propertyTable.addEntry(e, newClass->size);
newClass->nameMap.add(newClass->size, identifier);
newClass->propertyData.add(newClass->size, data);
++newClass->size;
- if (data.isAccessor()) {
- // add a dummy entry, since we need two entries for accessors
- newClass->propertyTable.addEntry(e, newClass->size);
- newClass->nameMap.add(newClass->size, 0);
- newClass->propertyData.add(newClass->size, PropertyAttributes());
- ++newClass->size;
- }
+ if (data.isAccessor())
+ addDummyEntry(newClass, e);
t.lookup = newClass;
Q_ASSERT(t.lookup);
return newClass;
}
-void InternalClass::removeMember(Object *object, Identifier *id)
+void InternalClass::removeChildEntry(InternalClass *child)
{
- InternalClass *oldClass = object->internalClass();
- uint propIdx = oldClass->propertyTable.lookup(id);
- Q_ASSERT(propIdx < oldClass->size);
+ Q_ASSERT(engine);
+ for (auto &t : transitions) {
+ if (t.lookup == child) {
+ t.lookup = nullptr;
+ return;
+ }
+ }
+ Q_UNREACHABLE();
- Transition temp = { { id }, nullptr, -1 };
- Transition &t = object->internalClass()->lookupOrInsertTransition(temp);
+}
- bool accessor = oldClass->propertyData.at(propIdx).isAccessor();
+void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier)
+{
+ Heap::InternalClass *oldClass = object->internalClass();
+ Q_ASSERT(oldClass->propertyTable.lookup(identifier) < oldClass->size);
- if (t.lookup) {
- object->setInternalClass(t.lookup);
- } else {
+ Transition temp = { { identifier }, nullptr, Transition::RemoveMember };
+ Transition &t = object->internalClass()->lookupOrInsertTransition(temp);
+
+ if (!t.lookup) {
// create a new class and add it to the tree
- InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable);
- newClass = newClass->changePrototype(oldClass->prototype);
- for (uint i = 0; i < oldClass->size; ++i) {
- if (i == propIdx)
- continue;
- if (!oldClass->propertyData.at(i).isEmpty())
- newClass = newClass->addMember(oldClass->nameMap.at(i), oldClass->propertyData.at(i));
- }
- object->setInternalClass(newClass);
+ Heap::InternalClass *newClass = oldClass->engine->newClass(oldClass);
+ // simply make the entry inaccessible
+ int idx = newClass->propertyTable.removeIdentifier(identifier, oldClass->size);
+ newClass->nameMap.set(idx, PropertyKey::invalid());
+ newClass->propertyData.set(idx, PropertyAttributes());
+ t.lookup = newClass;
+ Q_ASSERT(t.lookup);
}
+ object->setInternalClass(t.lookup);
- Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1));
-
- // remove the entry in the property data
- removeFromPropertyData(object, propIdx, accessor);
-
- t.lookup = object->internalClass();
- Q_ASSERT(t.lookup);
+ // we didn't remove the data slot, just made it inaccessible
+ Q_ASSERT(object->internalClass()->size == oldClass->size);
}
-uint InternalClass::find(const String *string)
+Heap::InternalClass *InternalClass::sealed()
{
- engine->identifierTable->identifier(string);
- const Identifier *id = string->d()->identifier;
+ if (isSealed)
+ return this;
- uint index = propertyTable.lookup(id);
- if (index < size)
- return index;
+ bool alreadySealed = !extensible;
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if (attrs.isConfigurable()) {
+ alreadySealed = false;
+ break;
+ }
+ }
- return UINT_MAX;
-}
+ if (alreadySealed) {
+ isSealed = true;
+ return this;
+ }
-InternalClass *InternalClass::sealed()
-{
- if (m_sealed)
- return m_sealed;
+ Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed };
+ Transition &t = lookupOrInsertTransition(temp);
+
+ if (t.lookup) {
+ Q_ASSERT(t.lookup && t.lookup->isSealed);
+ return t.lookup;
+ }
+
+ Heap::InternalClass *s = engine->newClass(this);
- m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- m_sealed = m_sealed->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (attrs.isEmpty())
continue;
attrs.setConfigurable(false);
- m_sealed = m_sealed->addMember(nameMap.at(i), attrs);
+ s->propertyData.set(i, attrs);
}
- m_sealed = m_sealed->nonExtensible();
+ s->extensible = false;
+ s->isSealed = true;
- m_sealed->m_sealed = m_sealed;
- return m_sealed;
+ t.lookup = s;
+ return s;
}
-InternalClass *InternalClass::frozen()
+Heap::InternalClass *InternalClass::frozen()
{
- if (m_frozen)
- return m_frozen;
+ if (isFrozen)
+ return this;
+
+ bool alreadyFrozen = !extensible;
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) {
+ alreadyFrozen = false;
+ break;
+ }
+ }
+
+ if (alreadyFrozen) {
+ isSealed = true;
+ isFrozen = true;
+ return this;
+ }
+
+ Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen };
+ Transition &t = lookupOrInsertTransition(temp);
+
+ if (t.lookup) {
+ Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen);
+ return t.lookup;
+ }
+
+ Heap::InternalClass *f = engine->newClass(this);
- m_frozen = propertiesFrozen();
- m_frozen = m_frozen->nonExtensible();
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if (attrs.isData())
+ attrs.setWritable(false);
+ attrs.setConfigurable(false);
+ f->propertyData.set(i, attrs);
+ }
+ f->extensible = false;
+ f->isSealed = true;
+ f->isFrozen = true;
- m_frozen->m_frozen = m_frozen;
- m_frozen->m_sealed = m_frozen;
- return m_frozen;
+ t.lookup = f;
+ return f;
}
-InternalClass *InternalClass::propertiesFrozen() const
+Heap::InternalClass *InternalClass::propertiesFrozen() const
{
- InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable));
frozen = frozen->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
@@ -455,20 +582,20 @@ InternalClass *InternalClass::propertiesFrozen() const
attrs.setConfigurable(false);
frozen = frozen->addMember(nameMap.at(i), attrs);
}
- return frozen;
+ return frozen->d();
}
-InternalClass *InternalClass::asProtoClass()
+Heap::InternalClass *InternalClass::asProtoClass()
{
if (isUsedAsProto)
return this;
- Transition temp = { { nullptr }, nullptr, Transition::ProtoClass };
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
newClass->isUsedAsProto = true;
t.lookup = newClass;
@@ -476,90 +603,43 @@ InternalClass *InternalClass::asProtoClass()
return newClass;
}
-void InternalClass::destroy()
+static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
{
- std::vector<InternalClass *> destroyStack;
- destroyStack.reserve(64);
- destroyStack.push_back(this);
-
- while (!destroyStack.empty()) {
- InternalClass *next = destroyStack.back();
- destroyStack.pop_back();
- if (!next->engine)
- continue;
- next->engine = nullptr;
- next->propertyTable.~PropertyHash();
- next->nameMap.~SharedInternalClassData<Identifier *>();
- next->propertyData.~SharedInternalClassData<PropertyAttributes>();
- if (next->m_sealed)
- destroyStack.push_back(next->m_sealed);
- if (next->m_frozen)
- destroyStack.push_back(next->m_frozen);
-
- for (size_t i = 0; i < next->transitions.size(); ++i) {
- Q_ASSERT(next->transitions.at(i).lookup);
- destroyStack.push_back(next->transitions.at(i).lookup);
- }
-
- next->transitions.~vector<Transition>();
+ if (ic->prototype == o)
+ ic->protoId = ic->engine->newProtoId();
+ for (auto &t : ic->transitions) {
+ if (t.lookup)
+ updateProtoUsage(o, t.lookup);
}
}
+
void InternalClass::updateProtoUsage(Heap::Object *o)
{
Q_ASSERT(isUsedAsProto);
- InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty];
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty);
Q_ASSERT(!ic->prototype);
- // only need to go two levels into the IC hierarchy, as prototype changes
- // can only happen there
- for (auto &t : ic->transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange) {
- InternalClass *ic2 = t.lookup;
- for (auto &t2 : ic2->transitions) {
- if (t2.flags == InternalClassTransition::PrototypeChange &&
- t2.lookup->prototype == o)
- ic2->updateInternalClassIdRecursive();
- }
- } else if (t.flags == InternalClassTransition::PrototypeChange && t.lookup->prototype == o) {
- ic->updateInternalClassIdRecursive();
- }
- }
+ Heap::updateProtoUsage(o, ic);
}
-void InternalClass::updateInternalClassIdRecursive()
+void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
{
- id = engine->newInternalClassId();
- for (auto &t : transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange || t.flags == InternalClassTransition::PrototypeChange)
- continue;
- t.lookup->updateInternalClassIdRecursive();
+ Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
+ if (ic->prototype)
+ ic->prototype->mark(stack);
+ if (ic->parent)
+ ic->parent->mark(stack);
+
+ for (uint i = 0; i < ic->size; ++i) {
+ PropertyKey id = ic->nameMap.at(i);
+ if (Heap::Base *b = id.asStringOrSymbol())
+ b->mark(stack);
}
}
+}
-
-void InternalClassPool::markObjects(MarkStack *markStack)
-{
- InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty];
- Q_ASSERT(!ic->prototype);
-
- // only need to go two levels into the IC hierarchy, as prototype changes
- // can only happen there
- for (auto &t : ic->transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange) {
- InternalClass *ic2 = t.lookup;
- for (auto &t2 : ic2->transitions) {
- if (t2.flags == InternalClassTransition::PrototypeChange)
- t2.lookup->prototype->mark(markStack);
- }
- } else if (t.flags == InternalClassTransition::PrototypeChange) {
- t.lookup->prototype->mark(markStack);
- }
- }
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index b689272006..888eedcaba 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -53,16 +53,13 @@
#include "qv4global_p.h"
#include <QHash>
-#include <private/qqmljsmemorypool_p.h>
-#include <private/qv4identifier_p.h>
+#include <private/qv4propertykey_p.h>
+#include <private/qv4heap_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-struct String;
-struct Object;
-struct Identifier;
struct VTable;
struct MarkStack;
@@ -70,7 +67,7 @@ struct PropertyHashData;
struct PropertyHash
{
struct Entry {
- const Identifier *identifier;
+ PropertyKey identifier;
uint index;
};
@@ -79,12 +76,12 @@ struct PropertyHash
inline PropertyHash();
inline PropertyHash(const PropertyHash &other);
inline ~PropertyHash();
+ PropertyHash &operator=(const PropertyHash &other);
void addEntry(const Entry &entry, int classSize);
- uint lookup(const Identifier *identifier) const;
-
-private:
- PropertyHash &operator=(const PropertyHash &other);
+ uint lookup(PropertyKey identifier) const;
+ int removeIdentifier(PropertyKey identifier, int classSize);
+ void detach(bool grow, int classSize);
};
struct PropertyHashData
@@ -118,15 +115,26 @@ inline PropertyHash::~PropertyHash()
delete d;
}
-inline uint PropertyHash::lookup(const Identifier *identifier) const
+inline PropertyHash &PropertyHash::operator=(const PropertyHash &other)
+{
+ ++other.d->refCount;
+ if (!--d->refCount)
+ delete d;
+ d = other.d;
+ return *this;
+}
+
+
+
+inline uint PropertyHash::lookup(PropertyKey identifier) const
{
Q_ASSERT(d->entries);
- uint idx = identifier->hashValue % d->alloc;
+ uint idx = identifier.id() % d->alloc;
while (1) {
if (d->entries[idx].identifier == identifier)
return d->entries[idx].index;
- if (!d->entries[idx].identifier)
+ if (!d->entries[idx].identifier.isValid())
return UINT_MAX;
++idx;
idx %= d->alloc;
@@ -163,6 +171,13 @@ struct SharedInternalClassData {
if (!--d->refcount)
delete d;
}
+ SharedInternalClassData &operator=(const SharedInternalClassData &other) {
+ ++other.d->refcount;
+ if (!--d->refcount)
+ delete d;
+ d = other.d;
+ return *this;
+ }
void add(uint pos, T value) {
if (pos < d->size) {
@@ -214,26 +229,26 @@ struct SharedInternalClassData {
Q_ASSERT(i < d->size);
return d->data[i];
}
-
-private:
- SharedInternalClassData &operator=(const SharedInternalClassData &other);
};
struct InternalClassTransition
{
union {
- Identifier *id;
+ PropertyKey id;
const VTable *vtable;
Heap::Object *prototype;
};
- InternalClass *lookup;
+ Heap::InternalClass *lookup;
int flags;
enum {
// range 0-0xff is reserved for attribute changes
NotExtensible = 0x100,
VTableChange = 0x200,
PrototypeChange = 0x201,
- ProtoClass = 0x202
+ ProtoClass = 0x202,
+ Sealed = 0x203,
+ Frozen = 0x204,
+ RemoveMember = -1
};
bool operator==(const InternalClassTransition &other) const
@@ -243,48 +258,44 @@ struct InternalClassTransition
{ return id < other.id || (id == other.id && flags < other.flags); }
};
-struct InternalClass : public QQmlJS::Managed {
- int id = 0; // unique across the engine, gets changed also when proto chain changes
+namespace Heap {
+
+struct InternalClass : Base {
ExecutionEngine *engine;
const VTable *vtable;
+ quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes
Heap::Object *prototype;
+ InternalClass *parent;
PropertyHash propertyTable; // id to valueIndex
- SharedInternalClassData<Identifier *> nameMap;
+ SharedInternalClassData<PropertyKey> nameMap;
SharedInternalClassData<PropertyAttributes> propertyData;
typedef InternalClassTransition Transition;
std::vector<Transition> transitions;
InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
- InternalClass *m_sealed;
- InternalClass *m_frozen;
-
uint size;
bool extensible;
- bool isUsedAsProto = false;
+ bool isSealed;
+ bool isFrozen;
+ bool isUsedAsProto;
+
+ void init(ExecutionEngine *engine);
+ void init(InternalClass *other);
+ void destroy();
Q_REQUIRED_RESULT InternalClass *nonExtensible();
- Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
- if (vtable == vt)
- return this;
- return changeVTableImpl(vt);
- }
- Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
- if (prototype == proto)
- return this;
- return changePrototypeImpl(proto);
- }
- static void addMember(Object *object, String *string, PropertyAttributes data, uint *index);
- Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = nullptr);
- Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
- Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
- static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = nullptr);
- static void removeMember(Object *object, Identifier *id);
- uint find(const String *string);
- uint find(const Identifier *id)
+ static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index);
+ Q_REQUIRED_RESULT InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, uint *index = nullptr);
+ Q_REQUIRED_RESULT InternalClass *changeMember(PropertyKey identifier, PropertyAttributes data, uint *index = nullptr);
+ static void changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, uint *index = nullptr);
+ static void removeMember(QV4::Object *object, PropertyKey identifier);
+ uint find(const PropertyKey id)
{
+ Q_ASSERT(id.isStringOrSymbol());
+
uint index = propertyTable.lookup(id);
if (index < size)
return index;
@@ -298,24 +309,37 @@ struct InternalClass : public QQmlJS::Managed {
Q_REQUIRED_RESULT InternalClass *asProtoClass();
- void destroy();
+ Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
+ if (vtable == vt)
+ return this;
+ return changeVTableImpl(vt);
+ }
+ Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
+ if (prototype == proto)
+ return this;
+ return changePrototypeImpl(proto);
+ }
void updateProtoUsage(Heap::Object *o);
+ static void markObjects(Heap::Base *ic, MarkStack *stack);
+
private:
Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt);
Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto);
- InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index);
- void updateInternalClassIdRecursive();
+ InternalClass *addMemberImpl(PropertyKey identifier, PropertyAttributes data, uint *index);
+
+ void removeChildEntry(InternalClass *child);
friend struct ExecutionEngine;
- InternalClass(ExecutionEngine *engine);
- InternalClass(const InternalClass &other);
};
-struct InternalClassPool : public QQmlJS::MemoryPool
+inline
+void Base::markObjects(Base *b, MarkStack *stack)
{
- void markObjects(MarkStack *markStack);
-};
+ b->internalClass->mark(stack);
+}
+
+}
}
diff --git a/src/qml/jsruntime/qv4iterator.cpp b/src/qml/jsruntime/qv4iterator.cpp
new file mode 100644
index 0000000000..df8000a8f7
--- /dev/null
+++ b/src/qml/jsruntime/qv4iterator.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qv4iterator_p.h>
+#include <qv4symbol_p.h>
+#include <qv4engine_p.h>
+
+using namespace QV4;
+
+void IteratorPrototype::init(ExecutionEngine *engine)
+{
+ defineDefaultProperty(engine->symbol_iterator(), method_iterator, 0);
+}
+
+ReturnedValue IteratorPrototype::method_iterator(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ return thisObject->asReturnedValue();
+}
+
+
+ReturnedValue IteratorPrototype::createIterResultObject(ExecutionEngine *engine, const Value &value, bool done)
+{
+ Scope scope(engine);
+ ScopedObject obj(scope, engine->newObject());
+ obj->set(ScopedString(scope, engine->newString(QStringLiteral("value"))), value, Object::DoNotThrow);
+ obj->set(ScopedString(scope, engine->newString(QStringLiteral("done"))), Primitive::fromBoolean(done), Object::DoNotThrow);
+ return obj->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4iterator_p.h b/src/qml/jsruntime/qv4iterator_p.h
new file mode 100644
index 0000000000..28e337d21b
--- /dev/null
+++ b/src/qml/jsruntime/qv4iterator_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ITERATOR_P_H
+#define QV4ITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4arraydata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+enum IteratorKind {
+ KeyIteratorKind,
+ ValueIteratorKind,
+ KeyValueIteratorKind
+};
+
+struct IteratorPrototype : Object
+{
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_iterator(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue createIterResultObject(ExecutionEngine *engine, const Value &value, bool done);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index c676b57c51..307eec9111 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -55,6 +55,7 @@
#include "qv4functionobject_p.h"
#include "qv4context_p.h"
#include "qv4scopedvalue_p.h"
+#include "qv4stackframe_p.h"
QT_BEGIN_NAMESPACE
@@ -67,7 +68,7 @@ struct JSCallData {
if (thisObject)
this->thisObject = const_cast<Value *>(thisObject);
else
- this->thisObject = scope.alloc(1);
+ this->thisObject = scope.alloc();
if (argv)
this->args = const_cast<Value *>(argv);
else
@@ -80,8 +81,7 @@ struct JSCallData {
CallData *callData(const FunctionObject *f = nullptr) const {
int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc;
- CallData *ptr = reinterpret_cast<CallData *>(scope.engine->jsStackTop);
- scope.engine->jsStackTop += size;
+ CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size));
ptr->function = Encode::undefined();
ptr->context = Encode::undefined();
ptr->accumulator = Encode::undefined();
@@ -102,7 +102,9 @@ struct JSCallData {
inline
ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const
{
- return d()->jsConstruct(this, data.args, data.argc);
+ if (!d()->jsConstruct)
+ return engine()->throwTypeError(QStringLiteral("Object is not a constructor."));
+ return d()->jsConstruct(this, data.args, data.argc, this);
}
inline
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index c3569c29d2..2e2314cafe 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -47,6 +47,7 @@
#include <qv4variantobject_p.h>
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include <qv4symbol_p.h>
#include <qstack.h>
#include <qstringlist.h>
@@ -260,11 +261,12 @@ bool JsonParser::parseMember(Object *o)
if (!parseValue(val))
return false;
- ScopedString s(scope, engine->newIdentifier(key));
- uint idx = s->asArrayIndex();
- if (idx < UINT_MAX) {
- o->putIndexed(idx, val);
+ ScopedString s(scope, engine->newString(key));
+ PropertyKey skey = s->toPropertyKey();
+ if (skey.isArrayIndex()) {
+ o->put(skey.asArrayIndex(), val);
} else {
+ // avoid trouble with properties named __proto__
o->insertMember(s, val);
}
@@ -743,7 +745,7 @@ QString Stringify::Str(const QString &key, const Value &v)
o = value->asReturnedValue();
if (o) {
if (!o->as<FunctionObject>()) {
- if (o->as<ArrayObject>() || o->isListType()) {
+ if (o->isArrayLike()) {
return JA(o.getPointer());
} else {
return JO(o);
@@ -846,7 +848,7 @@ QString Stringify::JA(Object *a)
ScopedValue v(scope);
for (uint i = 0; i < len; ++i) {
bool exists;
- v = a->getIndexed(i, &exists);
+ v = a->get(i, &exists);
if (!exists) {
partial += QStringLiteral("null");
continue;
@@ -881,6 +883,8 @@ void Heap::JsonObject::init()
o->defineDefaultProperty(QStringLiteral("parse"), QV4::JsonObject::method_parse, 2);
o->defineDefaultProperty(QStringLiteral("stringify"), QV4::JsonObject::method_stringify, 3);
+ ScopedString json(scope, scope.engine->newString(QStringLiteral("JSON")));
+ o->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), json);
}
@@ -916,7 +920,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen));
for (uint i = 0; i < arrayLen; ++i) {
Value *v = stringify.propertyList + i;
- *v = o->getIndexed(i);
+ *v = o->get(i);
if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber())
*v = v->toString(scope.engine);
if (!v->isString()) {
@@ -1078,7 +1082,7 @@ QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObj
ScopedValue v(scope);
quint32 length = a->getLength();
for (quint32 i = 0; i < length; ++i) {
- v = a->getIndexed(i);
+ v = a->get(i);
if (v->as<FunctionObject>())
v = Encode::null();
result.append(toJsonValue(v, visitedObjects));
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 52ab03cd94..daa5b2dfbd 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
-void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto)
+void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
{
while (proto) {
uint index = proto->internalClass->find(name);
@@ -70,7 +70,12 @@ void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
Heap::Object *obj = object->d();
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ 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);
+ }
uint index = obj->internalClass->find(name);
if (index != UINT_MAX) {
@@ -92,7 +97,7 @@ ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *objec
return getter(this, engine, *object);
}
- protoLookup.icIdentifier = obj->internalClass->id;
+ protoLookup.protoId = obj->internalClass->protoId;
resolveProtoGetter(name, obj->prototype());
return getter(this, engine, *object);
}
@@ -109,11 +114,12 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
break;
case Value::Managed_Type: {
// ### Should move this over to the Object path, as strings also have an internalClass
- Q_ASSERT(object.isString());
- primitiveLookup.proto = engine->stringPrototype()->d();
+ Q_ASSERT(object.isStringOrSymbol());
+ primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype;
+ Q_ASSERT(primitiveLookup.proto);
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- if (name->equals(engine->id_length())) {
+ if (object.isString() && name->equals(engine->id_length())) {
// special case, as the property is on the object itself
getter = stringLengthGetter;
return stringLengthGetter(this, engine, object);
@@ -125,8 +131,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
primitiveLookup.proto = engine->numberPrototype()->d();
}
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- protoLookup.icIdentifier = primitiveLookup.proto->internalClass->id;
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ protoLookup.protoId = primitiveLookup.proto->internalClass->protoId;
resolveProtoGetter(name, primitiveLookup.proto);
if (getter == getterProto)
@@ -139,8 +145,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine)
{
Object *o = engine->globalObject;
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- protoLookup.icIdentifier = o->internalClass()->id;
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ protoLookup.protoId = o->internalClass()->protoId;
resolveProtoGetter(name, o->d());
if (getter == getterProto)
@@ -188,16 +194,16 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const
return result;
}
if (first.getter == getterProto && second.getter == getterProto) {
- l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier;
- l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier;
+ l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
+ l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
l->protoLookupTwoClasses.data = first.protoLookup.data;
l->protoLookupTwoClasses.data2 = second.protoLookup.data;
l->getter = getterProtoTwoClasses;
return result;
}
if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
- l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier;
- l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier;
+ l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
+ l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
l->protoLookupTwoClasses.data = first.protoLookup.data;
l->protoLookupTwoClasses.data2 = second.protoLookup.data;
l->getter = getterProtoAccessorTwoClasses;
@@ -250,7 +256,7 @@ ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Valu
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
- if (l->protoLookup.icIdentifier == o->internalClass->id)
+ if (l->protoLookup.protoId == o->internalClass->protoId)
return l->protoLookup.data->asReturnedValue();
}
return getterTwoClasses(l, engine, object);
@@ -307,9 +313,9 @@ ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
- if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
return l->protoLookupTwoClasses.data->asReturnedValue();
- if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
return l->protoLookupTwoClasses.data2->asReturnedValue();
return getterFallback(l, engine, object);
}
@@ -340,14 +346,13 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co
// we can safely cast to a QV4::Object here. If object is actually a string,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
- if (o && l->protoLookup.icIdentifier == o->internalClass->id) {
+ if (o && l->protoLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->protoLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0);
}
- l->getter = getterTwoClasses;
return getterTwoClasses(l, engine, object);
}
@@ -358,9 +363,9 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
const Value *getter = nullptr;
- if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
getter = l->protoLookupTwoClasses.data;
- else if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id)
+ else if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
getter = l->protoLookupTwoClasses.data2;
if (getter) {
if (!getter->isFunctionObject()) // ### catch at resolve time
@@ -373,11 +378,29 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *
return getterFallback(l, engine, object);
}
+ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ Object *o = object.objectValue();
+ if (o) {
+ Heap::Object *ho = o->d();
+ if (ho->arrayData && ho->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = ho->arrayData.cast<Heap::SimpleArrayData>();
+ if (l->indexedLookup.index < s->values.size)
+ if (!s->data(l->indexedLookup.index).isEmpty())
+ return s->data(l->indexedLookup.index).asReturnedValue();
+ }
+ return o->get(l->indexedLookup.index);
+ }
+ l->getter = getterFallback;
+ return getterFallback(l, engine, object);
+
+}
+
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->primitiveLookup.type) {
Heap::Object *o = l->primitiveLookup.proto;
- if (l->primitiveLookup.icIdentifier == o->internalClass->id)
+ if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
}
l->getter = getterGeneric;
@@ -388,7 +411,7 @@ ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine
{
if (object.type() == l->primitiveLookup.type) {
Heap::Object *o = l->primitiveLookup.proto;
- if (l->primitiveLookup.icIdentifier == o->internalClass->id) {
+ if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
@@ -417,7 +440,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
- if (l->protoLookup.icIdentifier == o->internalClass->id)
+ if (l->protoLookup.protoId == o->internalClass->protoId)
return l->protoLookup.data->asReturnedValue();
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -426,7 +449,7 @@ ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine)
ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
- if (l->protoLookup.icIdentifier == o->internalClass->id) {
+ if (l->protoLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->protoLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
@@ -442,8 +465,9 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
Scope scope(engine);
ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- InternalClass *c = object->internalClass();
- uint idx = c->find(name);
+ Heap::InternalClass *c = object->internalClass();
+ PropertyKey key = name->toPropertyKey();
+ uint idx = c->find(key);
if (idx != UINT_MAX) {
if (object->isArrayObject() && idx == Heap::ArrayObject::LengthPropertyIndex) {
setter = arrayLengthSetter;
@@ -460,8 +484,8 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
return setter(this, engine, *object, value);
}
- insertionLookup.icIdentifier = c->id;
- if (!object->put(name, value)) {
+ insertionLookup.protoId = c->protoId;
+ if (!object->put(key, value)) {
setter = Lookup::setterFallback;
return false;
}
@@ -471,7 +495,7 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
setter = setterFallback;
return true;
}
- idx = object->internalClass()->find(name);
+ idx = object->internalClass()->find(key);
if (idx == UINT_MAX) { // ### can this even happen?
setter = setterFallback;
return false;
@@ -574,7 +598,7 @@ bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c
bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
- if (o && o->internalClass()->id == l->insertionLookup.icIdentifier) {
+ if (o && o->internalClass()->protoId == l->insertionLookup.protoId) {
o->setInternalClass(l->insertionLookup.newClass);
o->d()->setProperty(engine, l->insertionLookup.offset, value);
return true;
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index 5f507733fd..c7555539b4 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -65,7 +65,6 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct Lookup {
- enum { Size = 4 };
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
@@ -73,44 +72,57 @@ struct Lookup {
};
union {
struct {
- InternalClass *ic;
+ Heap::Base *h1;
+ Heap::Base *h2;
+ quintptr unused;
+ quintptr unused2;
+ } markDef;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr _unused;
int offset;
} objectLookup;
struct {
+ quintptr protoId;
+ quintptr _unused;
const Value *data;
- int icIdentifier;
} protoLookup;
struct {
- InternalClass *ic;
- InternalClass *ic2;
+ Heap::InternalClass *ic;
+ Heap::InternalClass *ic2;
int offset;
int offset2;
} objectLookupTwoClasses;
struct {
+ quintptr protoId;
+ quintptr protoId2;
const Value *data;
const Value *data2;
- int icIdentifier;
- int icIdentifier2;
} protoLookupTwoClasses;
struct {
// Make sure the next two values are in sync with protoLookup
- const Value *data;
- int icIdentifier;
- unsigned type;
+ quintptr protoId;
Heap::Object *proto;
+ const Value *data;
+ quintptr type;
} primitiveLookup;
struct {
- InternalClass *newClass;
- int icIdentifier;
+ Heap::InternalClass *newClass;
+ quintptr protoId;
int offset;
} insertionLookup;
+ struct {
+ quintptr _unused;
+ quintptr _unused2;
+ uint index;
+ } indexedLookup;
};
uint nameIndex;
ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object);
ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object);
ReturnedValue resolveGlobalGetter(ExecutionEngine *engine);
- void resolveProtoGetter(Identifier *name, const Heap::Object *proto);
+ void resolveProtoGetter(PropertyKey name, const Heap::Object *proto);
static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -126,6 +138,7 @@ struct Lookup {
static ReturnedValue getterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -144,6 +157,17 @@ struct Lookup {
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+
+ void markObjects(MarkStack *stack) {
+ if (markDef.h1 && !(reinterpret_cast<quintptr>(markDef.h1) & 1))
+ markDef.h1->mark(stack);
+ if (markDef.h2 && !(reinterpret_cast<quintptr>(markDef.h2) & 1))
+ markDef.h2->mark(stack);
+ }
+
+ void clear() {
+ memset(&markDef, 0, sizeof(markDef));
+ }
};
Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index b50e5f0355..bb7b8086e4 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -43,34 +43,23 @@
using namespace QV4;
+DEFINE_MANAGED_VTABLE(Managed);
-const VTable Managed::static_vtbl =
-{
- nullptr,
- 0,
- 0,
- Managed::IsExecutionContext,
- Managed::IsString,
- Managed::IsObject,
- Managed::IsFunctionObject,
- Managed::IsErrorObject,
- Managed::IsArrayData,
- 0,
- Managed::MyType,
- "Managed",
- nullptr,
- nullptr /*markObjects*/,
- isEqualTo
-};
+DEFINE_MANAGED_VTABLE(InternalClass);
QString Managed::className() const
{
const char *s = nullptr;
- switch (Type(d()->vtable()->type)) {
+ switch (Type(vtable()->type)) {
case Type_Invalid:
- case Type_String:
return QString();
+ case Type_String:
+ s = "String";
+ break;
+ case Type_Symbol:
+ s = "Symbol";
+ break;
case Type_Object:
s = "Object";
break;
@@ -80,6 +69,9 @@ QString Managed::className() const
case Type_FunctionObject:
s = "Function";
break;
+ case Type_GeneratorObject:
+ s = "Generator";
+ break;
case Type_BooleanObject:
s = "Boolean";
break;
@@ -89,6 +81,9 @@ QString Managed::className() const
case Type_StringObject:
s = "String";
break;
+ case Type_SymbolObject:
+ s = "Symbol";
+ break;
case Type_DateObject:
s = "Date";
break;
@@ -96,7 +91,7 @@ QString Managed::className() const
s = "RegExp";
break;
case Type_ErrorObject:
- s = ErrorObject::className(static_cast<Heap::ErrorObject *>(d())->errorType);
+ s = "Error";
break;
case Type_ArgumentsObject:
s = "Arguments";
@@ -104,6 +99,9 @@ QString Managed::className() const
case Type_JsonObject:
s = "JSON";
break;
+ case Type_ProxyObject:
+ s = "ProxyObject";
+ break;
case Type_MathObject:
s = "Math";
break;
@@ -111,8 +109,23 @@ QString Managed::className() const
case Type_ExecutionContext:
s = "__ExecutionContext";
break;
- case Type_ForeachIteratorObject:
- s = "__ForeachIterator";
+ case Type_MapIteratorObject:
+ s = "Map Iterator";
+ break;
+ case Type_SetIteratorObject:
+ s = "Set Iterator";
+ break;
+ case Type_ArrayIteratorObject:
+ s = "Array Iterator";
+ break;
+ case Type_StringIteratorObject:
+ s = "String Iterator";
+ break;
+ case Type_ForInIterator:
+ s = "__ForIn Iterator";
+ break;
+ case Type_InternalClass:
+ s = "__InternalClass";
break;
case Type_RegExp:
s = "__RegExp";
@@ -125,7 +138,7 @@ QString Managed::className() const
return QString::fromLatin1(s);
}
-bool Managed::isEqualTo(Managed *, Managed *)
+bool Managed::virtualIsEqualTo(Managed *, Managed *)
{
return false;
}
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index 092c61b81c..cacb262ba7 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -55,6 +55,7 @@
#include "qv4enginebase_p.h"
#include <private/qv4heap_p.h>
#include <private/qv4writebarrier_p.h>
+#include <private/qv4vtable_p.h>
QT_BEGIN_NAMESPACE
@@ -70,13 +71,9 @@ inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; }
template <typename T1, typename T2>
inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
-#ifdef Q_COMPILER_STATIC_ASSERT
-#define V4_MANAGED_SIZE_TEST void __dataTest() { Q_STATIC_ASSERT(sizeof(*this) == sizeof(Managed)); }
-#else
-#define V4_MANAGED_SIZE_TEST
-#endif
+#define V4_MANAGED_SIZE_TEST void __dataTest() { static_assert (sizeof(*this) == sizeof(Managed), "Classes derived from Managed can't have own data members."); }
-#define V4_NEEDS_DESTROY static void destroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); }
+#define V4_NEEDS_DESTROY static void virtualDestroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); }
#define V4_MANAGED_ITSELF(DataClass, superClass) \
@@ -92,77 +89,30 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
QV4::Heap::DataClass *dptr = d_unchecked(); \
dptr->_checkIsInitialized(); \
return dptr; \
- } \
- Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
+ }
#define V4_MANAGED(DataClass, superClass) \
private: \
DataClass() Q_DECL_EQ_DELETE; \
Q_DISABLE_COPY(DataClass) \
- V4_MANAGED_ITSELF(DataClass, superClass)
+ V4_MANAGED_ITSELF(DataClass, superClass) \
+ Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
#define Q_MANAGED_TYPE(type) \
public: \
enum { MyType = Type_##type };
-#define Q_VTABLE_FUNCTION(classname, func) \
- (classname::func == QV4::Managed::func ? 0 : classname::func)
-
-// Q_VTABLE_FUNCTION triggers a bogus tautological-compare warning in GCC6+
-#if (defined(Q_CC_GNU) && Q_CC_GNU >= 600)
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
- QT_WARNING_PUSH; \
- QT_WARNING_DISABLE_GCC("-Wtautological-compare")
-
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \
- ;QT_WARNING_POP
-#elif defined(Q_CC_CLANG) && Q_CC_CLANG >= 306
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
- QT_WARNING_PUSH; \
- QT_WARNING_DISABLE_CLANG("-Wtautological-compare")
-
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \
- ;QT_WARNING_POP
-#else
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-#endif
-
-#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
-{ \
- parentVTable, \
- (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
- (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \
- - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
- classname::IsExecutionContext, \
- classname::IsString, \
- classname::IsObject, \
- classname::IsFunctionObject, \
- classname::IsErrorObject, \
- classname::IsArrayData, \
- 0, \
- classname::MyType, \
- #classname, \
- Q_VTABLE_FUNCTION(classname, destroy), \
- classname::Data::markObjects, \
- isEqualTo \
-} \
-
-#define DEFINE_MANAGED_VTABLE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
-const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
#define V4_INTERNALCLASS(c) \
- static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
- { return e->internalClasses[QV4::EngineBase::Class_##c]; }
+ static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
+ { return e->internalClasses(QV4::EngineBase::Class_##c); }
-struct Q_QML_PRIVATE_EXPORT Managed : Value
+struct Q_QML_PRIVATE_EXPORT Managed : Value, VTableBase
{
V4_MANAGED_ITSELF(Base, Managed)
enum {
IsExecutionContext = false,
IsString = false,
+ IsStringOrSymbol = false,
IsObject = false,
IsFunctionObject = false,
IsErrorObject = false,
@@ -180,47 +130,55 @@ public:
Type_Invalid,
Type_String,
Type_Object,
+ Type_Symbol,
Type_ArrayObject,
Type_FunctionObject,
+ Type_GeneratorObject,
Type_BooleanObject,
Type_NumberObject,
Type_StringObject,
+ Type_SymbolObject,
Type_DateObject,
Type_RegExpObject,
Type_ErrorObject,
Type_ArgumentsObject,
Type_JsonObject,
Type_MathObject,
+ Type_ProxyObject,
Type_ExecutionContext,
- Type_ForeachIteratorObject,
+ Type_InternalClass,
+ Type_SetIteratorObject,
+ Type_MapIteratorObject,
+ Type_ArrayIteratorObject,
+ Type_StringIteratorObject,
+ Type_ForInIterator,
Type_RegExp,
Type_QmlSequence
};
Q_MANAGED_TYPE(Invalid)
- InternalClass *internalClass() const { return d()->internalClass; }
+ Heap::InternalClass *internalClass() const { return d()->internalClass; }
+ const VTable *vtable() const { return d()->internalClass->vtable; }
inline ExecutionEngine *engine() const { return internalClass()->engine; }
- bool isListType() const { return d()->vtable()->type == Type_QmlSequence; }
+ bool isListType() const { return d()->internalClass->vtable->type == Type_QmlSequence; }
+ bool isArrayLike() const { return isArrayObject() || isListType(); }
- bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; }
- bool isStringObject() const { return d()->vtable()->type == Type_StringObject; }
+ bool isArrayObject() const { return d()->internalClass->vtable->type == Type_ArrayObject; }
+ bool isStringObject() const { return d()->internalClass->vtable->type == Type_StringObject; }
+ bool isSymbolObject() const { return d()->internalClass->vtable->type == Type_SymbolObject; }
QString className() const;
bool isEqualTo(const Managed *other) const
- { return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
-
- static bool isEqualTo(Managed *m, Managed *other);
+ { return d()->internalClass->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
bool inUse() const { return d()->inUse(); }
bool markBit() const { return d()->isMarked(); }
inline void mark(MarkStack *markStack);
- static void destroy(Heap::Base *) {}
-
Q_ALWAYS_INLINE Heap::Base *heapObject() const {
return m();
}
@@ -232,6 +190,9 @@ public:
return static_cast<const T *>(this);
}
+protected:
+ static bool virtualIsEqualTo(Managed *m, Managed *other);
+
private:
friend class MemoryManager;
friend struct Identifiers;
@@ -254,6 +215,29 @@ inline const Object *Value::as() const {
return objectValue();
}
+
+struct InternalClass : Managed
+{
+ V4_MANAGED_ITSELF(InternalClass, Managed)
+ Q_MANAGED_TYPE(InternalClass)
+ V4_INTERNALCLASS(Empty)
+ V4_NEEDS_DESTROY
+
+ Q_REQUIRED_RESULT Heap::InternalClass *changeVTable(const VTable *vt) {
+ return d()->changeVTable(vt);
+ }
+ Q_REQUIRED_RESULT Heap::InternalClass *changePrototype(Heap::Object *proto) {
+ return d()->changePrototype(proto);
+ }
+ Q_REQUIRED_RESULT Heap::InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, uint *index = 0) {
+ return d()->addMember(identifier, data, index);
+ }
+
+ void operator =(Heap::InternalClass *ic) {
+ Value::operator=(ic);
+ }
+};
+
}
diff --git a/src/qml/jsruntime/qv4mapiterator.cpp b/src/qml/jsruntime/qv4mapiterator.cpp
new file mode 100644
index 0000000000..7be7416e4a
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapiterator.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4estable_p.h>
+#include <private/qv4mapiterator_p.h>
+#include <private/qv4mapobject_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(MapIteratorObject);
+
+void MapIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Map Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue MapIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const MapIteratorObject *thisObject = that->as<MapIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not a Map Iterator instance"));
+
+ Scoped<MapObject> s(scope, thisObject->d()->iteratedMap);
+ uint index = thisObject->d()->mapNextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ if (!s) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ Value *arguments = scope.alloc(2);
+
+ while (index < s->d()->esTable->size()) {
+ s->d()->esTable->iterate(index, &arguments[0], &arguments[1]);
+ thisObject->d()->mapNextIndex = index + 1;
+
+ ScopedValue result(scope);
+
+ if (itemKind == KeyIteratorKind) {
+ result = arguments[0];
+ } else if (itemKind == ValueIteratorKind) {
+ result = arguments[1];
+ } else {
+ Q_ASSERT(itemKind == KeyValueIteratorKind);
+
+ result = scope.engine->newArrayObject();
+
+ Scoped<ArrayObject> resultArray(scope, result);
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, arguments[0]);
+ resultArray->arrayPut(1, arguments[1]);
+ resultArray->setArrayLengthUnchecked(2);
+ }
+
+ return IteratorPrototype::createIterResultObject(scope.engine, result, false);
+ }
+
+ thisObject->d()->iteratedMap.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+}
+
+
diff --git a/src/qml/jsruntime/qv4mapiterator_p.h b/src/qml/jsruntime/qv4mapiterator_p.h
new file mode 100644
index 0000000000..836ba14663
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapiterator_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MAPITERATOR_P_H
+#define QV4MAPITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4iterator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define MapIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedMap) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, mapNextIndex)
+
+DECLARE_HEAP_OBJECT(MapIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(MapIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedMap.set(engine, obj);
+ this->mapNextIndex = 0;
+ }
+};
+
+}
+
+struct MapIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapIteratorObject : Object
+{
+ V4_OBJECT2(MapIteratorObject, Object)
+ Q_MANAGED_TYPE(MapIteratorObject)
+ V4_PROTOTYPE(mapIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4MAPITERATOR_P_H
+
+
diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp
new file mode 100644
index 0000000000..ca9e1723f9
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapobject.cpp
@@ -0,0 +1,279 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4setobject_p.h" // ### temporary
+#include "qv4mapobject_p.h"
+#include "qv4mapiterator_p.h"
+#include "qv4estable_p.h"
+#include "qv4symbol_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(MapCtor);
+DEFINE_OBJECT_VTABLE(MapObject);
+
+void Heap::MapCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Map"));
+}
+
+ReturnedValue MapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ Scope scope(f);
+ Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>());
+
+ if (argc > 0) {
+ ScopedValue iterable(scope, argv[0]);
+
+ // ### beware, hack alert!
+ // Object iteration seems broken right now. if we allow any object to
+ // iterate, it endlessly loops in the Map/prototype tests in test262...
+ // disable these for now until Object iteration is fixed, just so we can
+ // test this.
+ Scoped<MapObject> mapObjectCheck(scope, argv[0]);
+ Scoped<SetObject> setObjectCheck(scope, argv[0]);
+
+ if (!iterable->isUndefined() && !iterable->isNull() && (mapObjectCheck || setObjectCheck)) {
+ ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("set")))));
+ if (!adder)
+ return scope.engine->throwTypeError();
+ ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+
+ CHECK_EXCEPTION();
+ if (!iter)
+ return a.asReturnedValue();
+
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+ forever {
+ done = Runtime::method_iteratorNext(scope.engine, iter, nextValue);
+ CHECK_EXCEPTION();
+ if (done->toBoolean())
+ return a.asReturnedValue();
+
+ adder->call(a, nextValue, 1);
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ }
+ }
+ }
+ }
+ return a.asReturnedValue();
+}
+
+ReturnedValue MapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ return scope.engine->throwTypeError(QString::fromLatin1("Map requires new"));
+}
+
+void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ ctor->addSymbolSpecies();
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("clear"), method_clear, 0);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(QStringLiteral("get"), method_get, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 0);
+ defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr);
+ defineDefaultProperty(QStringLiteral("values"), method_values, 0);
+
+ // Per the spec, the value for entries/@@iterator is the same
+ ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries")));
+ ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, MapPrototype::method_entries, 0));
+ defineDefaultProperty(QStringLiteral("entries"), entriesFn);
+ defineDefaultProperty(engine->symbol_iterator(), entriesFn);
+
+ ScopedString val(scope, engine->newString(QLatin1String("Map")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+void Heap::MapObject::init()
+{
+ Object::init();
+ esTable = new ESTable();
+}
+
+void Heap::MapObject::destroy()
+{
+ delete esTable;
+ esTable = 0;
+}
+
+void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack)
+{
+ MapObject *m = static_cast<MapObject *>(that);
+ m->esTable->markObjects(markStack);
+ Object::markObjects(that, markStack);
+}
+
+ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->clear();
+ return Encode::undefined();
+}
+
+ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->remove(argv[0]));
+}
+
+ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject callbackfn(scope, argv[0]);
+ if (!callbackfn)
+ return scope.engine->throwTypeError();
+
+ ScopedValue thisArg(scope, Primitive::undefinedValue());
+ if (argc > 1)
+ thisArg = ScopedValue(scope, argv[1]);
+
+ Value *arguments = scope.alloc(3);
+ for (uint i = 0; i < that->d()->esTable->size(); ++i) {
+ that->d()->esTable->iterate(i, &arguments[0], &arguments[1]); // fill in key (0), value (1)
+
+ arguments[2] = that;
+ callbackfn->call(thisArg, arguments, 3);
+ CHECK_EXCEPTION();
+ }
+ return Encode::undefined();
+}
+
+ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ return that->d()->esTable->get(argv[0]);
+}
+
+ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->has(argv[0]));
+}
+
+ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argv[0], argv[1]);
+ return that.asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->size());
+}
+
+ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+
diff --git a/src/qml/jsruntime/qv4mapobject_p.h b/src/qml/jsruntime/qv4mapobject_p.h
new file mode 100644
index 0000000000..6793612bcb
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapobject_p.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MAPOBJECT_P_H
+#define QV4MAPOBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+class ESTable;
+
+namespace Heap {
+
+struct MapCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct MapObject : Object {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
+ void init();
+ void destroy();
+ ESTable *esTable;
+};
+
+}
+
+struct MapCtor: FunctionObject
+{
+ V4_OBJECT2(MapCtor, FunctionObject)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapObject : Object
+{
+ V4_OBJECT2(MapObject, Object)
+ V4_PROTOTYPE(mapPrototype)
+ V4_NEEDS_DESTROY
+};
+
+struct MapPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+} // namespace QV4
+
+
+QT_END_NAMESPACE
+
+#endif // QV4MAPOBJECT_P_H
+
diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp
index 0c18d908de..e176235786 100644
--- a/src/qml/jsruntime/qv4mathobject.cpp
+++ b/src/qml/jsruntime/qv4mathobject.cpp
@@ -39,6 +39,7 @@
#include "qv4mathobject_p.h"
#include "qv4objectproto_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/qdatetime.h>
#include <QtCore/qmath.h>
@@ -70,14 +71,27 @@ void Heap::MathObject::init()
m->defineDefaultProperty(QStringLiteral("abs"), QV4::MathObject::method_abs, 1);
m->defineDefaultProperty(QStringLiteral("acos"), QV4::MathObject::method_acos, 1);
- m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 0);
+ m->defineDefaultProperty(QStringLiteral("acosh"), QV4::MathObject::method_acosh, 1);
+ m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 1);
+ m->defineDefaultProperty(QStringLiteral("asinh"), QV4::MathObject::method_asinh, 1);
m->defineDefaultProperty(QStringLiteral("atan"), QV4::MathObject::method_atan, 1);
+ m->defineDefaultProperty(QStringLiteral("atanh"), QV4::MathObject::method_atanh, 1);
m->defineDefaultProperty(QStringLiteral("atan2"), QV4::MathObject::method_atan2, 2);
+ m->defineDefaultProperty(QStringLiteral("cbrt"), QV4::MathObject::method_cbrt, 1);
m->defineDefaultProperty(QStringLiteral("ceil"), QV4::MathObject::method_ceil, 1);
+ m->defineDefaultProperty(QStringLiteral("clz32"), QV4::MathObject::method_clz32, 1);
m->defineDefaultProperty(QStringLiteral("cos"), QV4::MathObject::method_cos, 1);
+ m->defineDefaultProperty(QStringLiteral("cosh"), QV4::MathObject::method_cosh, 1);
m->defineDefaultProperty(QStringLiteral("exp"), QV4::MathObject::method_exp, 1);
+ m->defineDefaultProperty(QStringLiteral("expm1"), QV4::MathObject::method_expm1, 1);
m->defineDefaultProperty(QStringLiteral("floor"), QV4::MathObject::method_floor, 1);
+ m->defineDefaultProperty(QStringLiteral("fround"), QV4::MathObject::method_fround, 1);
+ m->defineDefaultProperty(QStringLiteral("hypot"), QV4::MathObject::method_hypot, 2);
+ m->defineDefaultProperty(QStringLiteral("imul"), QV4::MathObject::method_imul, 2);
m->defineDefaultProperty(QStringLiteral("log"), QV4::MathObject::method_log, 1);
+ m->defineDefaultProperty(QStringLiteral("log10"), QV4::MathObject::method_log10, 1);
+ m->defineDefaultProperty(QStringLiteral("log1p"), QV4::MathObject::method_log1p, 1);
+ m->defineDefaultProperty(QStringLiteral("log2"), QV4::MathObject::method_log2, 1);
m->defineDefaultProperty(QStringLiteral("max"), QV4::MathObject::method_max, 2);
m->defineDefaultProperty(QStringLiteral("min"), QV4::MathObject::method_min, 2);
m->defineDefaultProperty(QStringLiteral("pow"), QV4::MathObject::method_pow, 2);
@@ -85,8 +99,14 @@ void Heap::MathObject::init()
m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1);
m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1);
m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1);
+ m->defineDefaultProperty(QStringLiteral("sinh"), QV4::MathObject::method_sinh, 1);
m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1);
m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1);
+ m->defineDefaultProperty(QStringLiteral("tanh"), QV4::MathObject::method_tanh, 1);
+ m->defineDefaultProperty(QStringLiteral("trunc"), QV4::MathObject::method_trunc, 1);
+
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("Math")));
+ m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
}
static Q_ALWAYS_INLINE double copySign(double x, double y)
@@ -120,6 +140,19 @@ ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::acos(v)));
}
+ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : 2;
+ if (v < 1)
+ RETURN_RESULT(Encode(qt_qnan()));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::log(v +std::sqrt(v + 1) * std::sqrt(v - 1))));
+#else
+ RETURN_RESULT(Encode(std::acosh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : 2;
@@ -129,6 +162,19 @@ ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::asin(v)));
}
+ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : 2;
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::log(v +std::sqrt(1 + v * v))));
+#else
+ RETURN_RESULT(Encode(std::asinh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -138,6 +184,25 @@ ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::atan(v)));
}
+ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ if (-1 < v && v < 1)
+ RETURN_RESULT(Encode(0.5 * (std::log(v + 1) - std::log(v - 1))));
+
+ if (v > 1 || v < -1)
+ RETURN_RESULT(Encode(qt_qnan()));
+
+ RETURN_RESULT(Encode(copySign(qt_inf(), v)));
+#else
+ RETURN_RESULT(Encode(std::atanh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v1 = argc ? argv[0].toNumber() : qt_qnan();
@@ -156,6 +221,16 @@ ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, co
RETURN_RESULT(Encode(std::atan2(v1, v2)));
}
+ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(copySign(std::exp(std::log(std::abs(v)) / 3), v)));
+#else
+ RETURN_RESULT(Encode(std::cbrt(v))); // cube root
+#endif
+}
+
ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -165,12 +240,24 @@ ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::ceil(v)));
}
+ReturnedValue MathObject::method_clz32(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ quint32 v = argc ? argv[0].toUInt32() : 0;
+ RETURN_RESULT(Encode(qint32(qCountLeadingZeroBits(v))));
+}
+
ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
RETURN_RESULT(Encode(std::cos(v)));
}
+ReturnedValue MathObject::method_cosh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ RETURN_RESULT(Encode(std::cosh(v)));
+}
+
ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -184,6 +271,25 @@ ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, cons
}
}
+ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (std::isnan(v) || qIsNull(v)) {
+ RETURN_RESULT(Encode(v));
+ } else if (qt_is_inf(v)) {
+ if (copySign(1.0, v) == -1.0)
+ RETURN_RESULT(Encode(-1.0));
+ else
+ RETURN_RESULT(Encode(qt_inf()));
+ } else {
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::exp(v) - 1));
+#else
+ RETURN_RESULT(Encode(std::expm1(v)));
+#endif
+ }
+}
+
ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -192,6 +298,53 @@ ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, co
RETURN_RESULT(result);
}
+ReturnedValue MathObject::method_fround(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+ else // convert to 32-bit float using roundTiesToEven, then convert back to 64-bit double
+ RETURN_RESULT(Encode(double(float(v))));
+}
+
+ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to
+ // avoid the loss of precision from overflows and underflows" (as std::hypot does).
+ double v = argc ? argv[0].toNumber() : 0;
+ // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ...
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ bool big = qt_is_inf(v), bad = std::isnan(v);
+ v *= v;
+ for (int i = 1; !big && i < argc; i++) {
+ double u = argv[i].toNumber();
+ if (qt_is_inf(u))
+ big = true;
+ if (std::isnan(u))
+ bad = true;
+ v += u * u;
+ }
+ if (big)
+ RETURN_RESULT(Encode(qt_inf()));
+ if (bad)
+ RETURN_RESULT(Encode(qt_qnan()));
+ // Should actually check for {und,ov}erflow, but too fiddly !
+ RETURN_RESULT(Primitive::fromDouble(sqrt(v)));
+#else
+ for (int i = 1; i < argc; i++)
+ v = std::hypot(v, argv[i].toNumber());
+#endif
+ RETURN_RESULT(Primitive::fromDouble(v));
+}
+
+ReturnedValue MathObject::method_imul(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ quint32 a = argc ? argv[0].toUInt32() : 0;
+ quint32 b = argc > 0 ? argv[1].toUInt32() : 0;
+ qint32 product = a * b;
+ RETURN_RESULT(Encode(product));
+}
+
ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -201,6 +354,43 @@ ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, cons
RETURN_RESULT(Encode(std::log(v)));
}
+ReturnedValue MathObject::method_log10(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < 0)
+ RETURN_RESULT(Encode(qt_qnan()));
+ else
+ RETURN_RESULT(Encode(std::log10(v)));
+}
+
+ReturnedValue MathObject::method_log1p(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+#if !defined(__ANDROID__)
+ using std::log1p;
+#endif
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < -1)
+ RETURN_RESULT(Encode(qt_qnan()));
+ else
+ RETURN_RESULT(Encode(log1p(v)));
+}
+
+ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < 0) {
+ RETURN_RESULT(Encode(qt_qnan()));
+ } else {
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ // Android ndk r10e doesn't have std::log2, so fall back.
+ const double ln2 = std::log(2.0);
+ RETURN_RESULT(Encode(std::log(v) / ln2));
+#else
+ RETURN_RESULT(Encode(std::log2(v)));
+#endif
+ }
+}
+
ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double mx = -qt_inf();
@@ -209,7 +399,7 @@ ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, cons
if (x > mx || std::isnan(x))
mx = x;
}
- RETURN_RESULT(Encode(mx));
+ RETURN_RESULT(Encode::smallestNumber(mx));
}
ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -222,7 +412,7 @@ ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, cons
mx = x;
}
}
- RETURN_RESULT(Encode(mx));
+ RETURN_RESULT(Encode::smallestNumber(mx));
}
ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -283,8 +473,11 @@ ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, c
ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
- v = copySign(std::floor(v + 0.5), v);
- RETURN_RESULT(Encode(v));
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+
+ v = copySign(std::floor(v + 0.5), v);
+ RETURN_RESULT(Encode(v));
}
ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -303,7 +496,19 @@ ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, con
ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
- RETURN_RESULT(Encode(std::sin(v)));
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::sin(v)));
+}
+
+ReturnedValue MathObject::method_sinh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::sinh(v)));
}
ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -321,3 +526,25 @@ ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, cons
RETURN_RESULT(Encode(std::tan(v)));
}
+ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::tanh(v)));
+}
+
+ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+ // Nearest integer not greater in magnitude:
+ quint64 whole = std::abs(v);
+ RETURN_RESULT(Encode(copySign(whole, v)));
+#else
+ RETURN_RESULT(Encode(std::trunc(v)));
+#endif
+}
diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h
index 0bf5da9404..2658e25438 100644
--- a/src/qml/jsruntime/qv4mathobject_p.h
+++ b/src/qml/jsruntime/qv4mathobject_p.h
@@ -71,14 +71,27 @@ struct MathObject: Object
static ReturnedValue method_abs(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_acos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_acosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_asin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_asinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_atan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_atanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_atan2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_cbrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_ceil(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_clz32(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_cos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_cosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_exp(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_expm1(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_floor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fround(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_hypot(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_imul(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_log(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log10(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log1p(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_max(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_min(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_pow(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -86,8 +99,11 @@ struct MathObject: Object
static ReturnedValue method_round(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sqrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_tan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_tanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_trunc(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h
index ac9671254d..186083b83a 100644
--- a/src/qml/jsruntime/qv4memberdata_p.h
+++ b/src/qml/jsruntime/qv4memberdata_p.h
@@ -74,18 +74,6 @@ struct MemberData : Managed
V4_MANAGED(MemberData, Managed)
V4_INTERNALCLASS(MemberData)
- struct Index {
- Heap::Base *base;
- Value *slot;
-
- void set(EngineBase *e, Value newVal) {
- WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue());
- }
- const Value *operator->() const { return slot; }
- const Value &operator*() const { return *slot; }
- bool isNull() const { return !slot; }
- };
-
const Value &operator[] (uint idx) const { return d()->values[idx]; }
const Value *data() const { return d()->values.data(); }
void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); }
diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp
index f58ff45801..d103f65d47 100644
--- a/src/qml/jsruntime/qv4numberobject.cpp
+++ b/src/qml/jsruntime/qv4numberobject.cpp
@@ -78,13 +78,13 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Number"));
}
-ReturnedValue NumberCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
double dbl = argc ? argv[0].toNumber() : 0.;
return Encode(f->engine()->newNumberObject(dbl));
}
-ReturnedValue NumberCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue NumberCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double dbl = argc ? argv[0].toNumber() : 0.;
return Encode(dbl);
@@ -95,7 +95,7 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qt_qnan()));
ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf()));
diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h
index cfdcf9bc1d..576817cf36 100644
--- a/src/qml/jsruntime/qv4numberobject_p.h
+++ b/src/qml/jsruntime/qv4numberobject_p.h
@@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject
{
V4_OBJECT2(NumberCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct NumberPrototype: NumberObject
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 0c6cde84ad..e4d670d4f3 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -50,6 +50,7 @@
#include "qv4string_p.h"
#include "qv4identifiertable_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <stdint.h>
@@ -57,9 +58,9 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(Object);
-void Object::setInternalClass(InternalClass *ic)
+void Object::setInternalClass(Heap::InternalClass *ic)
{
- d()->internalClass = ic;
+ d()->internalClass.set(engine(), ic);
if (ic->isUsedAsProto)
ic->updateProtoUsage(d());
Q_ASSERT(ic && ic->vtable);
@@ -89,20 +90,7 @@ void Object::setProperty(uint index, const Property *p)
void Heap::Object::setUsedAsProto()
{
- internalClass = internalClass->asProtoClass();
-}
-
-bool Object::setPrototype(Object *proto)
-{
- Heap::Object *p = proto ? proto->d() : nullptr;
- Heap::Object *pp = p;
- while (pp) {
- if (pp == d())
- return false;
- pp = pp->prototype();
- }
- setInternalClass(internalClass()->changePrototype(p));
- return true;
+ internalClass.set(internalClass->engine, internalClass->asProtoClass());
}
ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs)
@@ -121,7 +109,7 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property
bool Object::putValue(uint memberIndex, const Value &value)
{
- QV4::InternalClass *ic = internalClass();
+ Heap::InternalClass *ic = internalClass();
if (ic->engine->hasException)
return false;
@@ -148,37 +136,34 @@ bool Object::putValue(uint memberIndex, const Value &value)
return true;
}
-void Object::defineDefaultProperty(const QString &name, const Value &value)
+void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
ScopedString s(scope, e->newIdentifier(name));
- defineDefaultProperty(s, value);
+ defineDefaultProperty(s, value, attributes);
}
-void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
+void Object::defineDefaultProperty(const QString &name, VTable::Call code,
+ int argumentCount, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
ScopedString s(scope, e->newIdentifier(name));
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, s, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(s, function);
+ ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, s, code, argumentCount));
+ defineDefaultProperty(s, function, attributes);
}
-void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
+void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, VTable::Call code,
+ int argumentCount, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, name, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(name, function);
+ ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, nameOrSymbol, code, argumentCount));
+ defineDefaultProperty(nameOrSymbol, function, attributes);
}
-void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
+void Object::defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter)
{
ExecutionEngine *e = engine();
Scope scope(e);
@@ -186,16 +171,27 @@ void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)
defineAccessorProperty(s, getter, setter);
}
-void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
+void Object::defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter)
{
ExecutionEngine *v4 = engine();
QV4::Scope scope(v4);
ScopedProperty p(scope);
- ExecutionContext *global = v4->rootContext();
- p->setGetter(ScopedFunctionObject(scope, (getter ? FunctionObject::createBuiltinFunction(global, name, getter) : nullptr)));
- p->setSetter(ScopedFunctionObject(scope, (setter ? FunctionObject::createBuiltinFunction(global, name, setter) : nullptr)));
- insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
+ QString n = name->toQString();
+ if (n.at(0) == QLatin1Char('@'))
+ n = QChar::fromLatin1('[') + n.midRef(1) + QChar::fromLatin1(']');
+ if (getter) {
+ ScopedString getName(scope, v4->newString(QString::fromLatin1("get ") + n));
+ p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0)));
+ } else {
+ p->setGetter(nullptr);
+ }
+ if (setter) {
+ ScopedString setName(scope, v4->newString(QString::fromLatin1("set ") + n));
+ p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0)));
+ } else {
+ p->setSetter(nullptr);
+ }
+ insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable);
}
@@ -221,13 +217,23 @@ void Object::defineReadonlyConfigurableProperty(const QString &name, const Value
defineReadonlyConfigurableProperty(s, value);
}
-void Object::defineReadonlyConfigurableProperty(String *name, const Value &value)
+void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value)
{
insertMember(name, value, Attr_ReadOnly_ButConfigurable);
}
+void Object::addSymbolSpecies()
+{
+ Scope scope(engine());
+ ScopedProperty p(scope);
+ p->setGetter(scope.engine->getSymbolSpecies());
+ p->setSetter(nullptr);
+ insertMember(scope.engine->symbol_species(), p, QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable);
+}
+
void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
{
+ Base::markObjects(b, stack);
Object *o = static_cast<Object *>(b);
if (o->memberData)
o->memberData->mark(stack);
@@ -242,10 +248,11 @@ void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
}
}
-void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes)
+void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes)
{
uint idx;
- InternalClass::addMember(this, s, attributes, &idx);
+ PropertyKey key = s->toPropertyKey();
+ Heap::InternalClass::addMember(this, key, attributes, &idx);
if (attributes.isAccessor()) {
setProperty(idx + GetterOffset, p->value);
@@ -255,230 +262,81 @@ void Object::insertMember(String *s, const Property *p, PropertyAttributes attri
}
}
-// Section 8.12.1
-void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p)
+void Object::setPrototypeUnchecked(const Object *p)
{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return getOwnProperty(idx, attrs, p);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- uint member = internalClass()->find(id);
- if (member < UINT_MAX) {
- *attrs = internalClass()->propertyData[member];
- if (p) {
- p->value = *propertyData(member);
- if (attrs->isAccessor())
- p->set = *propertyData(member + SetterOffset);
- }
- return;
- }
-
- if (attrs)
- *attrs = Attr_Invalid;
- return;
-}
-
-void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p)
-{
- if (arrayData()) {
- if (arrayData()->getProperty(index, p, attrs))
- return;
- }
- if (isStringObject()) {
- *attrs = Attr_NotConfigurable|Attr_NotWritable;
- if (p)
- p->value = static_cast<StringObject *>(this)->getIndex(index);
- return;
- }
-
- if (attrs)
- *attrs = Attr_Invalid;
- return;
+ setInternalClass(internalClass()->changePrototype(p ? p->d() : nullptr));
}
// Section 8.12.2
-MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs)
-{
- Q_ASSERT(name->asArrayIndex() == UINT_MAX);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- Heap::Object *o = d();
- while (o) {
- uint idx = o->internalClass->find(id);
- if (idx < UINT_MAX) {
- *attrs = o->internalClass->propertyData[idx];
- return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx );
- }
-
- o = o->prototype();
- }
- *attrs = Attr_Invalid;
- return { nullptr, nullptr };
-}
-
-ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs)
+PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs)
{
- Heap::Object *o = d();
- while (o) {
- if (o->arrayData) {
- uint idx = o->arrayData->mappedIndex(index);
- if (idx != UINT_MAX) {
- *attrs = o->arrayData->attributes(index);
- return { o->arrayData , attrs->isAccessor() ? idx + SetterOffset : idx };
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ Heap::Object *o = d();
+ while (o) {
+ if (o->arrayData) {
+ uint idx = o->arrayData->mappedIndex(index);
+ if (idx != UINT_MAX) {
+ *attrs = o->arrayData->attributes(index);
+ return { o->arrayData , o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) };
+ }
}
+ if (o->vtable()->type == Type_StringObject) {
+ if (index < static_cast<const Heap::StringObject *>(o)->length()) {
+ // this is an evil hack, but it works, as the method is only ever called from put,
+ // where we don't use the returned pointer there for non writable attributes
+ *attrs = (Attr_NotWritable|Attr_NotConfigurable);
+ return { reinterpret_cast<Heap::ArrayData *>(0x1), nullptr };
+ }
+ }
+ o = o->prototype();
}
- if (o->vtable()->type == Type_StringObject) {
- if (index < static_cast<const Heap::StringObject *>(o)->length()) {
- // this is an evil hack, but it works, as the method is only ever called from putIndexed,
- // where we don't use the returned pointer there for non writable attributes
- *attrs = (Attr_NotWritable|Attr_NotConfigurable);
- return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 };
+ } else {
+ Heap::Object *o = d();
+ while (o) {
+ uint idx = o->internalClass->find(id);
+ if (idx < UINT_MAX) {
+ *attrs = o->internalClass->propertyData[idx];
+ return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx );
}
+
+ o = o->prototype();
}
- o = o->prototype();
}
*attrs = Attr_Invalid;
- return { nullptr, 0 };
-}
-
-bool Object::hasProperty(String *name) const
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return hasProperty(idx);
-
- Scope scope(engine());
- ScopedObject o(scope, d());
- while (o) {
- if (o->hasOwnProperty(name))
- return true;
-
- o = o->prototype();
- }
-
- return false;
-}
-
-bool Object::hasProperty(uint index) const
-{
- Scope scope(engine());
- ScopedObject o(scope, d());
- while (o) {
- if (o->hasOwnProperty(index))
- return true;
-
- o = o->prototype();
- }
-
- return false;
-}
-
-bool Object::hasOwnProperty(String *name) const
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return hasOwnProperty(idx);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- if (internalClass()->find(id) < UINT_MAX)
- return true;
- if (!query(name).isEmpty())
- return true;
- return false;
-}
-
-bool Object::hasOwnProperty(uint index) const
-{
- if (arrayData() && !arrayData()->isEmpty(index))
- return true;
-
- if (isStringObject()) {
- if (index < static_cast<const StringObject *>(this)->length())
- return true;
- }
- if (!queryIndexed(index).isEmpty())
- return true;
- return false;
+ return { nullptr, nullptr };
}
-ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int)
+ReturnedValue Object::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
{
return f->engine()->throwTypeError();
}
-ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *, int)
+ReturnedValue Object::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
{
return f->engine()->throwTypeError();
}
-ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- return static_cast<const Object *>(m)->internalGet(name, hasProperty);
+ if (id.isArrayIndex())
+ return static_cast<const Object *>(m)->internalGetIndexed(id.asArrayIndex(), receiver, hasProperty);
+ Scope scope(m);
+ Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol());
+ return static_cast<const Object *>(m)->internalGet(name, receiver, hasProperty);
}
-ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty)
+bool Object::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty);
+ return static_cast<Object *>(m)->internalPut(id, value, receiver);
}
-bool Object::put(Managed *m, String *name, const Value &value)
+bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
{
- return static_cast<Object *>(m)->internalPut(name, value);
+ return static_cast<Object *>(m)->internalDeleteProperty(id);
}
-bool Object::putIndexed(Managed *m, uint index, const Value &value)
-{
- return static_cast<Object *>(m)->internalPutIndexed(index, value);
-}
-
-PropertyAttributes Object::query(const Managed *m, String *name)
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return queryIndexed(m, idx);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- const Object *o = static_cast<const Object *>(m);
- idx = o->internalClass()->find(id);
- if (idx < UINT_MAX)
- return o->internalClass()->propertyData[idx];
-
- return Attr_Invalid;
-}
-
-PropertyAttributes Object::queryIndexed(const Managed *m, uint index)
-{
- const Object *o = static_cast<const Object *>(m);
- if (o->arrayData() && !o->arrayData()->isEmpty(index))
- return o->arrayData()->attributes(index);
-
- if (o->isStringObject()) {
- if (index < static_cast<const StringObject *>(o)->length())
- return (Attr_NotWritable|Attr_NotConfigurable);
- }
- return Attr_Invalid;
-}
-
-bool Object::deleteProperty(Managed *m, String *name)
-{
- return static_cast<Object *>(m)->internalDeleteProperty(name);
-}
-
-bool Object::deleteIndexedProperty(Managed *m, uint index)
-{
- return static_cast<Object *>(m)->internalDeleteIndexedProperty(index);
-}
-
-void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs)
+void Object::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs)
{
Object *o = static_cast<Object *>(m);
name->setM(nullptr);
@@ -525,9 +383,10 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *
}
while (it->memberIndex < o->internalClass()->size) {
- Identifier *n = o->internalClass()->nameMap.at(it->memberIndex);
- if (!n) {
+ PropertyKey n = o->internalClass()->nameMap.at(it->memberIndex);
+ if (!n.isStringOrSymbol() || !n.asStringOrSymbol()->internalClass->vtable->isString) {
// accessor properties have a dummy entry with n == 0
+ // symbol entries are supposed to be skipped
++it->memberIndex;
continue;
}
@@ -536,7 +395,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *
PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex];
++it->memberIndex;
if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
- name->setM(o->engine()->identifierTable->stringFromIdentifier(n));
+ name->setM(n.asStringOrSymbol());
*attrs = a;
pd->value = *o->propertyData(idx);
if (a.isAccessor())
@@ -549,14 +408,11 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *
}
// Section 8.12.3
-ReturnedValue Object::internalGet(String *name, bool *hasProperty) const
+ReturnedValue Object::internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const
{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return getIndexed(idx, hasProperty);
+ PropertyKey id = name->toPropertyKey();
- name->makeIdentifier();
- Identifier *id = name->identifier();
+ Q_ASSERT(!id.isArrayIndex());
Heap::Object *o = d();
while (o) {
@@ -564,7 +420,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const
if (idx < UINT_MAX) {
if (hasProperty)
*hasProperty = true;
- return getValue(*o->propertyData(idx), o->internalClass->propertyData.at(idx));
+ return Object::getValue(*receiver, *o->propertyData(idx), o->internalClass->propertyData.at(idx));
}
o = o->prototype();
@@ -575,7 +431,7 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const
return Encode::undefined();
}
-ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const
+ReturnedValue Object::internalGetIndexed(uint index, const Value *receiver, bool *hasProperty) const
{
PropertyAttributes attrs;
Scope scope(engine());
@@ -596,13 +452,13 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const
return str.asReturnedValue();
}
}
- o = o->prototype();
+ o = o->getPrototypeOf();
}
if (exists) {
if (hasProperty)
*hasProperty = true;
- return getValue(pd->value, attrs);
+ return Object::getValue(*receiver, pd->value, attrs);
}
if (hasProperty)
@@ -612,36 +468,44 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const
// Section 8.12.5
-bool Object::internalPut(String *name, const Value &value)
+bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver)
{
ExecutionEngine *engine = this->engine();
if (engine->hasException)
return false;
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return putIndexed(idx, value);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
+ uint index = id.asArrayIndex();
+ Scope scope(engine);
- MemberData::Index memberIndex{nullptr, nullptr};
- uint member = internalClass()->find(id);
PropertyAttributes attrs;
- if (member < UINT_MAX) {
- attrs = internalClass()->propertyData[member];
- memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member);
+ PropertyIndex propertyIndex{nullptr, nullptr};
+
+ if (index != UINT_MAX) {
+ if (arrayData())
+ propertyIndex = arrayData()->getValueOrSetter(index, &attrs);
+
+ if (propertyIndex.isNull() && isStringObject()) {
+ if (index < static_cast<StringObject *>(this)->length())
+ // not writable
+ return false;
+ }
+ } else {
+ uint member = internalClass()->find(id);
+ if (member < UINT_MAX) {
+ attrs = internalClass()->propertyData[member];
+ propertyIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member);
+ }
}
// clause 1
- if (!memberIndex.isNull()) {
+ if (!propertyIndex.isNull()) {
if (attrs.isAccessor()) {
- if (memberIndex->as<FunctionObject>())
+ if (propertyIndex->as<FunctionObject>())
goto cont;
return false;
} else if (!attrs.isWritable())
return false;
- else if (isArrayObject() && name->equals(engine->id_length())) {
+ else if (isArrayObject() && id == engine->id_length()->propertyKey()) {
bool ok;
uint l = value.asArrayLength(&ok);
if (!ok) {
@@ -652,19 +516,18 @@ bool Object::internalPut(String *name, const Value &value)
if (!ok)
return false;
} else {
- memberIndex.set(engine, value);
+ propertyIndex.set(engine, value);
}
return true;
- } else if (!prototype()) {
+ } else if (!getPrototypeOf()) {
if (!isExtensible())
return false;
} else {
// clause 4
- Scope scope(engine);
- memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs);
- if (!memberIndex.isNull()) {
+ propertyIndex = ScopedObject(scope, getPrototypeOf())->getValueOrSetter(id, &attrs);
+ if (!propertyIndex.isNull()) {
if (attrs.isAccessor()) {
- if (!memberIndex->as<FunctionObject>())
+ if (!propertyIndex->as<FunctionObject>())
return false;
} else if (!isExtensible() || !attrs.isWritable()) {
return false;
@@ -677,228 +540,59 @@ bool Object::internalPut(String *name, const Value &value)
cont:
// Clause 5
- if (!memberIndex.isNull() && attrs.isAccessor()) {
- Q_ASSERT(memberIndex->as<FunctionObject>());
+ if (!propertyIndex.isNull() && attrs.isAccessor()) {
+ Q_ASSERT(propertyIndex->as<FunctionObject>());
Scope scope(engine);
- ScopedFunctionObject setter(scope, *memberIndex);
+ ScopedFunctionObject setter(scope, *propertyIndex);
JSCallData jsCallData(scope, 1);
jsCallData->args[0] = value;
- *jsCallData->thisObject = this;
+ *jsCallData->thisObject = *receiver;
setter->call(jsCallData);
return !engine->hasException;
}
- insertMember(name, value);
- return true;
-}
-
-bool Object::internalPutIndexed(uint index, const Value &value)
-{
- ExecutionEngine *engine = this->engine();
- if (engine->hasException)
- return false;
-
- PropertyAttributes attrs;
-
- ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ nullptr, 0 };
-
- if (arrayIndex.isNull() && isStringObject()) {
- if (index < static_cast<StringObject *>(this)->length())
- // not writable
- return false;
- }
-
- // clause 1
- if (!arrayIndex.isNull()) {
- if (attrs.isAccessor()) {
- if (arrayIndex->as<FunctionObject>())
- goto cont;
- return false;
- } else if (!attrs.isWritable())
- return false;
-
- arrayIndex.set(engine, value);
- return true;
- } else if (!prototype()) {
- if (!isExtensible())
- return false;
+ if (index != UINT_MAX) {
+ arraySet(index, value);
} else {
- // clause 4
- Scope scope(engine);
- arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs);
- if (!arrayIndex.isNull()) {
- if (attrs.isAccessor()) {
- if (!arrayIndex->as<FunctionObject>())
- return false;
- } else if (!isExtensible() || !attrs.isWritable()) {
- return false;
- }
- } else if (!isExtensible()) {
- return false;
- }
- }
-
- cont:
-
- // Clause 5
- if (!arrayIndex.isNull() && attrs.isAccessor()) {
- Q_ASSERT(arrayIndex->as<FunctionObject>());
-
- Scope scope(engine);
- ScopedFunctionObject setter(scope, *arrayIndex);
- JSCallData jsCallData(scope, 1);
- jsCallData->args[0] = value;
- *jsCallData->thisObject = this;
- setter->call(jsCallData);
- return !engine->hasException;
+ Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol());
+ insertMember(name, value);
}
-
- arraySet(index, value);
return true;
}
// Section 8.12.7
-bool Object::internalDeleteProperty(String *name)
+bool Object::internalDeleteProperty(PropertyKey id)
{
if (internalClass()->engine->hasException)
return false;
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return deleteIndexedProperty(idx);
-
- name->makeIdentifier();
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ Scope scope(engine());
+ if (scope.engine->hasException)
+ return false;
- uint memberIdx = internalClass()->find(name->identifier());
- if (memberIdx != UINT_MAX) {
- if (internalClass()->propertyData[memberIdx].isConfigurable()) {
- InternalClass::removeMember(this, name->identifier());
+ Scoped<ArrayData> ad(scope, arrayData());
+ if (!ad || ad->vtable()->del(this, index))
return true;
- }
- return false;
- }
- return true;
-}
-
-bool Object::internalDeleteIndexedProperty(uint index)
-{
- Scope scope(engine());
- if (scope.engine->hasException)
return false;
+ }
- Scoped<ArrayData> ad(scope, arrayData());
- if (!ad || ad->vtable()->del(this, index))
- return true;
-
- return false;
-}
-
-// Section 8.12.9
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs)
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return __defineOwnProperty__(engine, idx, p, attrs);
-
- Scope scope(engine);
- name->makeIdentifier();
-
- uint memberIndex;
-
- if (isArrayObject() && name->equals(engine->id_length())) {
- Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length()));
- ScopedProperty lp(scope);
- PropertyAttributes cattrs;
- getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs);
- if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs))
+ uint memberIdx = internalClass()->find(id);
+ if (memberIdx != UINT_MAX) {
+ if (internalClass()->propertyData[memberIdx].isConfigurable()) {
+ Heap::InternalClass::removeMember(this, id);
return true;
- if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
- return false;
- bool succeeded = true;
- if (attrs.type() == PropertyAttributes::Data) {
- bool ok;
- uint l = p->value.asArrayLength(&ok);
- if (!ok) {
- ScopedValue v(scope, p->value);
- engine->throwRangeError(v);
- return false;
- }
- succeeded = setArrayLength(l);
}
- if (attrs.hasWritable() && !attrs.isWritable()) {
- cattrs.setWritable(false);
- InternalClass::changeMember(this, engine->id_length(), cattrs);
- }
- if (!succeeded)
- return false;
- return true;
- }
-
- // Clause 1
- memberIndex = internalClass()->find(name->identifier());
-
- if (memberIndex == UINT_MAX) {
- // clause 3
- if (!isExtensible())
- return false;
- // clause 4
- ScopedProperty pd(scope);
- pd->copy(p, attrs);
- pd->fullyPopulated(&attrs);
- insertMember(name, pd, attrs);
- return true;
- }
-
- return __defineOwnProperty__(engine, memberIndex, name, p, attrs);
-}
-
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs)
-{
- // 15.4.5.1, 4b
- if (isArrayObject() && index >= getLength() && !internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
return false;
-
- if (ArgumentsObject::isNonStrictArgumentsObject(this))
- return static_cast<ArgumentsObject *>(this)->defineOwnProperty(engine, index, p, attrs);
-
- return defineOwnProperty2(engine, index, p, attrs);
-}
-
-bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs)
-{
- bool hasProperty = 0;
-
- // Clause 1
- if (arrayData()) {
- hasProperty = arrayData()->mappedIndex(index) != UINT_MAX;
- if (!hasProperty && isStringObject())
- hasProperty = (index < static_cast<StringObject *>(this)->length());
}
- if (!hasProperty) {
- // clause 3
- if (!isExtensible())
- return false;
- // clause 4
- Scope scope(engine);
- ScopedProperty pp(scope);
- pp->copy(p, attrs);
- pp->fullyPopulated(&attrs);
- if (attrs == Attr_Data) {
- ScopedValue v(scope, pp->value);
- arraySet(index, v);
- } else {
- arraySet(index, pp, attrs);
- }
- return true;
- }
-
- return __defineOwnProperty__(engine, index, nullptr, p, attrs);
+ return true;
}
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs)
+bool Object::internalDefineOwnProperty(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs)
{
// clause 5
if (attrs.isEmpty())
@@ -977,7 +671,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
current->merge(cattrs, p, attrs);
if (member) {
- InternalClass::changeMember(this, member, cattrs);
+ Heap::InternalClass::changeMember(this, member->propertyKey(), cattrs);
setProperty(index, current);
} else {
setArrayAttributes(index, cattrs);
@@ -986,15 +680,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
return true;
}
-
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs)
-{
- Scope scope(engine);
- ScopedString s(scope, engine->newString(name));
- return __defineOwnProperty__(engine, s, p, attrs);
-}
-
-
void Object::copyArrayData(Object *other)
{
Q_ASSERT(isArrayObject());
@@ -1007,7 +692,7 @@ void Object::copyArrayData(Object *other)
ScopedValue v(scope);
for (uint i = 0; i < len; ++i) {
- arraySet(i, (v = other->getIndexed(i)));
+ arraySet(i, (v = other->get(i)));
}
} else if (!other->arrayData()) {
;
@@ -1030,15 +715,15 @@ void Object::copyArrayData(Object *other)
setArrayLengthUnchecked(other->getLength());
}
-uint Object::getLength(const Managed *m)
+qint64 Object::virtualGetLength(const Managed *m)
{
Scope scope(static_cast<const Object *>(m)->engine());
ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length()));
- return v->toUInt32();
+ return v->toLength();
}
// 'var' is 'V' in 15.3.5.3.
-ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var)
+ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &var)
{
QV4::ExecutionEngine *engine = typeObject->internalClass()->engine;
@@ -1080,6 +765,141 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var)
return Encode(false);
}
+bool Object::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ Scope scope(m->engine());
+ ScopedObject o(scope, m);
+ ScopedProperty p(scope);
+ while (o) {
+ if (o->getOwnProperty(id, p) != Attr_Invalid)
+ return true;
+
+ o = o->getPrototypeOf();
+ }
+
+ return false;
+}
+
+PropertyAttributes Object::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
+{
+ PropertyAttributes attrs;
+ Object *o = static_cast<Object *>(m);
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ if (o->arrayData()) {
+ if (o->arrayData()->getProperty(index, p, &attrs))
+ return attrs;
+ }
+ } else {
+ Q_ASSERT(id.asStringOrSymbol());
+
+ uint member = o->internalClass()->find(id);
+ if (member < UINT_MAX) {
+ attrs = o->internalClass()->propertyData[member];
+ if (p) {
+ p->value = *o->propertyData(member);
+ if (attrs.isAccessor())
+ p->set = *o->propertyData(member + SetterOffset);
+ }
+ return attrs;
+ }
+ }
+
+ return Attr_Invalid;
+}
+
+bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
+{
+ Object *o = static_cast<Object *>(m);
+ Scope scope(o);
+
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+
+ bool hasProperty = false;
+
+ if (o->arrayData()) {
+ hasProperty = o->arrayData()->mappedIndex(index) != UINT_MAX;
+ if (!hasProperty && o->isStringObject())
+ hasProperty = (index < static_cast<StringObject *>(o)->length());
+ }
+
+ if (!hasProperty) {
+ if (!o->isExtensible())
+ return false;
+
+ ScopedProperty pp(scope);
+ pp->copy(p, attrs);
+ pp->fullyPopulated(&attrs);
+ if (attrs == Attr_Data) {
+ ScopedValue v(scope, pp->value);
+ o->arraySet(index, v);
+ } else {
+ o->arraySet(index, pp, attrs);
+ }
+ return true;
+ }
+
+ return o->internalDefineOwnProperty(scope.engine, index, nullptr, p, attrs);
+ }
+
+ uint memberIndex = o->internalClass()->find(id);
+ Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol());
+
+ if (memberIndex == UINT_MAX) {
+ if (!o->isExtensible())
+ return false;
+
+ ScopedProperty pd(scope);
+ pd->copy(p, attrs);
+ pd->fullyPopulated(&attrs);
+ o->insertMember(name, pd, attrs);
+ return true;
+ }
+
+ return o->internalDefineOwnProperty(scope.engine, memberIndex, name, p, attrs);
+}
+
+bool Object::virtualIsExtensible(const Managed *m)
+{
+ return m->d()->internalClass->extensible;
+}
+
+bool Object::virtualPreventExtensions(Managed *m)
+{
+ Q_ASSERT(m->isObject());
+ Object *o = static_cast<Object *>(m);
+ o->setInternalClass(o->internalClass()->nonExtensible());
+ return true;
+}
+
+Heap::Object *Object::virtualGetPrototypeOf(const Managed *m)
+{
+ return m->internalClass()->prototype;
+}
+
+bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto)
+{
+ Q_ASSERT(m->isObject());
+ Object *o = static_cast<Object *>(m);
+ Heap::Object *current = o->internalClass()->prototype;
+ Heap::Object *protod = proto ? proto->d() : nullptr;
+ if (current == protod)
+ return true;
+ if (!o->internalClass()->extensible)
+ return false;
+ Heap::Object *p = protod;
+ while (p) {
+ if (p == o->d())
+ return false;
+ if (p->vtable()->getPrototypeOf != Object::staticVTable()->getPrototypeOf)
+ break;
+ p = p->prototype();
+ }
+ o->setInternalClass(o->internalClass()->changePrototype(protod));
+ return true;
+}
+
bool Object::setArrayLength(uint newLen)
{
Q_ASSERT(isArrayObject());
@@ -1132,12 +952,10 @@ void Heap::ArrayObject::init(const QStringList &list)
a->setArrayLengthUnchecked(len);
}
-uint ArrayObject::getLength(const Managed *m)
+qint64 ArrayObject::virtualGetLength(const Managed *m)
{
const ArrayObject *a = static_cast<const ArrayObject *>(m);
- if (a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->isInteger())
- return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->integerValue();
- return Primitive::toUInt32(a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->doubleValue());
+ return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->toLength();
}
QStringList ArrayObject::toQStringList() const
@@ -1150,8 +968,62 @@ QStringList ArrayObject::toQStringList() const
uint length = getLength();
for (uint i = 0; i < length; ++i) {
- v = const_cast<ArrayObject *>(this)->getIndexed(i);
+ v = const_cast<ArrayObject *>(this)->get(i);
result.append(v->toQStringNoThrow());
}
return result;
}
+
+bool ArrayObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
+{
+ Q_ASSERT(m->isArrayObject());
+ ArrayObject *a = static_cast<ArrayObject *>(m);
+
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ uint len = a->getLength();
+ if (index >= len && !a->internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
+ return false;
+
+ bool succeeded = Object::virtualDefineOwnProperty(m, id, p, attrs);
+ if (!succeeded)
+ return false;
+
+ if (index >= len)
+ a->setArrayLengthUnchecked(index + 1);
+
+ return true;
+ }
+
+ ExecutionEngine *engine = m->engine();
+ if (id == engine->id_length()->propertyKey()) {
+ Scope scope(engine);
+ Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == a->internalClass()->find(engine->id_length()->propertyKey()));
+ ScopedProperty lp(scope);
+ PropertyAttributes cattrs;
+ a->getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs);
+ if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs))
+ return true;
+ if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
+ return false;
+ bool succeeded = true;
+ if (attrs.type() == PropertyAttributes::Data) {
+ bool ok;
+ uint l = p->value.asArrayLength(&ok);
+ if (!ok) {
+ ScopedValue v(scope, p->value);
+ engine->throwRangeError(v);
+ return false;
+ }
+ succeeded = a->setArrayLength(l);
+ }
+ if (attrs.hasWritable() && !attrs.isWritable()) {
+ cattrs.setWritable(false);
+ Heap::InternalClass::changeMember(a, engine->id_length()->propertyKey(), cattrs);
+ }
+ if (!succeeded)
+ return false;
+ return true;
+ }
+ return Object::virtualDefineOwnProperty(m, id, p, attrs);
+}
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 1731ae3c76..dea080f98a 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -74,6 +74,10 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
static void markObjects(Heap::Base *base, MarkStack *stack);
void init() { Base::init(); }
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const {
Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties);
return reinterpret_cast<const Value *>(this) + indexWithOffset;
@@ -90,15 +94,15 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
Q_ASSERT(index < vtable()->nInlineProperties);
Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index;
- WriteBarrier::write(e, this, prop->data_ptr(), b->asReturnedValue());
+ WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
- QV4::MemberData::Index writablePropertyData(uint index) {
+ PropertyIndex writablePropertyData(uint index) {
uint nInline = vtable()->nInlineProperties;
if (index < nInline)
- return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
+ return PropertyIndex{ this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
index -= nInline;
- return { memberData, memberData->values.values + index };
+ return PropertyIndex{ memberData, memberData->values.values + index };
}
const Value *propertyData(uint index) const {
@@ -134,76 +138,6 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
}
-#define V4_OBJECT2(DataClass, superClass) \
- private: \
- DataClass() Q_DECL_EQ_DELETE; \
- Q_DISABLE_COPY(DataClass) \
- public: \
- Q_MANAGED_CHECK \
- typedef QV4::Heap::DataClass Data; \
- typedef superClass SuperClass; \
- static const QV4::ObjectVTable static_vtbl; \
- static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \
- V4_MANAGED_SIZE_TEST \
- QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \
- QV4::Heap::DataClass *d() const { \
- QV4::Heap::DataClass *dptr = d_unchecked(); \
- dptr->_checkIsInitialized(); \
- return dptr; \
- } \
- Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
-
-#define V4_PROTOTYPE(p) \
- static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \
- { return e->p(); }
-
-struct ObjectVTable
-{
- VTable vTable;
- ReturnedValue (*call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- ReturnedValue (*callAsConstructor)(const FunctionObject *, const Value *argv, int argc);
- ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty);
- ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty);
- bool (*put)(Managed *, String *name, const Value &value);
- bool (*putIndexed)(Managed *, uint index, const Value &value);
- PropertyAttributes (*query)(const Managed *, String *name);
- PropertyAttributes (*queryIndexed)(const Managed *, uint index);
- bool (*deleteProperty)(Managed *m, String *name);
- bool (*deleteIndexedProperty)(Managed *m, uint index);
- uint (*getLength)(const Managed *m);
- void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var);
-};
-
-#define DEFINE_OBJECT_VTABLE_BASE(classname) \
-const QV4::ObjectVTable classname::static_vtbl = \
-{ \
- DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl.vTable), \
- call, \
- callAsConstructor, \
- get, \
- getIndexed, \
- put, \
- putIndexed, \
- query, \
- queryIndexed, \
- deleteProperty, \
- deleteIndexedProperty, \
- getLength, \
- advanceIterator, \
- instanceOf \
-}
-
-#define DEFINE_OBJECT_VTABLE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
-DEFINE_OBJECT_VTABLE_BASE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
-#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
-template<> DEFINE_OBJECT_VTABLE_BASE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
struct Q_QML_EXPORT Object: Managed {
V4_OBJECT2(Object, Object)
Q_MANAGED_TYPE(Object)
@@ -218,7 +152,7 @@ struct Q_QML_EXPORT Object: Managed {
SetterOffset = 1
};
- void setInternalClass(InternalClass *ic);
+ void setInternalClass(Heap::InternalClass *ic);
const Value *propertyData(uint index) const { return d()->propertyData(index); }
@@ -232,27 +166,21 @@ struct Q_QML_EXPORT Object: Managed {
void setProperty(ExecutionEngine *engine, uint index, Value v) const { d()->setProperty(engine, index, v); }
void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); }
- const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); }
- Heap::Object *prototype() const { return d()->prototype(); }
- bool setPrototype(Object *proto);
+ const VTable *vtable() const { return d()->vtable(); }
- void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = nullptr);
- void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = nullptr);
-
- MemberData::Index getValueOrSetter(String *name, PropertyAttributes *attrs);
- ArrayData::Index getValueOrSetter(uint index, PropertyAttributes *attrs);
+ PropertyAttributes getOwnProperty(PropertyKey id, Property *p = nullptr) {
+ return vtable()->getOwnProperty(this, id, p);
+ }
- bool hasProperty(String *name) const;
- bool hasProperty(uint index) const;
+ PropertyIndex getValueOrSetter(PropertyKey id, PropertyAttributes *attrs);
- bool hasOwnProperty(String *name) const;
- bool hasOwnProperty(uint index) const;
+ bool hasProperty(PropertyKey id) const {
+ return vtable()->hasProperty(this, id);
+ }
- bool __defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs);
- bool __defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs);
- bool __defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs);
- bool __defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs);
- bool defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs);
+ bool defineOwnProperty(PropertyKey id, const Property *p, PropertyAttributes attrs) {
+ return vtable()->defineOwnProperty(this, id, p, attrs);
+ }
//
// helpers
@@ -267,33 +195,39 @@ struct Q_QML_EXPORT Object: Managed {
bool putValue(uint memberIndex, const Value &value);
/* The spec default: Writable: true, Enumerable: false, Configurable: true */
- void defineDefaultProperty(String *name, const Value &value) {
- insertMember(name, value, Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(StringOrSymbol *name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable) {
+ insertMember(name, value, attributes);
}
- void defineDefaultProperty(const QString &name, const Value &value);
- void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
- void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
- void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
- void defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
+ void defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(const QString &name, VTable::Call code,
+ int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(StringOrSymbol *name, VTable::Call code,
+ int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter);
+ void defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter);
/* Fixed: Writable: false, Enumerable: false, Configurable: false */
void defineReadonlyProperty(const QString &name, const Value &value);
void defineReadonlyProperty(String *name, const Value &value);
/* Fixed: Writable: false, Enumerable: false, Configurable: true */
void defineReadonlyConfigurableProperty(const QString &name, const Value &value);
- void defineReadonlyConfigurableProperty(String *name, const Value &value);
+ void defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value);
+
+ void addSymbolSpecies();
- void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) {
+ void insertMember(StringOrSymbol *s, const Value &v, PropertyAttributes attributes = Attr_Data) {
Scope scope(engine());
ScopedProperty p(scope);
p->value = v;
insertMember(s, p, attributes);
}
- void insertMember(String *s, const Property *p, PropertyAttributes attributes);
+ void insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes);
- bool isExtensible() const { return d()->internalClass->extensible; }
+ bool isExtensible() const { return vtable()->isExtensible(this); }
+ bool preventExtensions() { return vtable()->preventExtensions(this); }
+ Heap::Object *getPrototypeOf() const { return vtable()->getPrototypeOf(this); }
+ bool setPrototypeOf(const Object *p) { return vtable()->setPrototypeOf(this, p); }
+ void setPrototypeUnchecked(const Object *p);
// Array handling
@@ -353,33 +287,58 @@ public:
Scope scope(engine());
ScopedObject p(scope, this);
- while ((p = p->prototype()))
+ while ((p = p->getPrototypeOf()))
if (p->arrayData())
return true;
return false;
}
- inline ReturnedValue get(String *name, bool *hasProperty = nullptr) const
- { return vtable()->get(this, name, hasProperty); }
- inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const
- { return vtable()->getIndexed(this, idx, hasProperty); }
+ inline ReturnedValue get(StringOrSymbol *name, bool *hasProperty = nullptr, const Value *receiver = nullptr) const
+ { if (!receiver) receiver = this; return vtable()->get(this, name->toPropertyKey(), receiver, hasProperty); }
+ inline ReturnedValue get(uint idx, bool *hasProperty = nullptr, const Value *receiver = nullptr) const
+ { if (!receiver) receiver = this; return vtable()->get(this, PropertyKey::fromArrayIndex(idx), receiver, hasProperty); }
+ QT_DEPRECATED inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const
+ { return get(idx, hasProperty); }
+ inline ReturnedValue get(PropertyKey id, const Value *receiver = nullptr, bool *hasProperty = nullptr) const
+ { if (!receiver) receiver = this; return vtable()->get(this, id, receiver, hasProperty); }
// use the set variants instead, to customize throw behavior
- inline bool put(String *name, const Value &v)
- { return vtable()->put(this, name, v); }
- inline bool putIndexed(uint idx, const Value &v)
- { return vtable()->putIndexed(this, idx, v); }
+ inline bool put(StringOrSymbol *name, const Value &v, Value *receiver = nullptr)
+ { if (!receiver) receiver = this; return vtable()->put(this, name->toPropertyKey(), v, receiver); }
+ inline bool put(uint idx, const Value &v, Value *receiver = nullptr)
+ { if (!receiver) receiver = this; return vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, receiver); }
+ QT_DEPRECATED inline bool putIndexed(uint idx, const Value &v)
+ { return put(idx, v); }
+ inline bool put(PropertyKey id, const Value &v, Value *receiver = nullptr)
+ { if (!receiver) receiver = this; return vtable()->put(this, id, v, receiver); }
enum ThrowOnFailure {
DoThrowOnRejection,
DoNotThrow
};
+ // This is the same as set(), but it doesn't require creating a string key,
+ // which is much more efficient for the array case.
+ inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow)
+ {
+ bool ret = vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, this);
+ // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
+ if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
+ ExecutionEngine *e = engine();
+ if (!e->hasException) { // allow a custom set impl to throw itself
+ QString message = QLatin1String("Cannot assign to read-only property \"") +
+ QString::number(idx) + QLatin1Char('\"');
+ e->throwTypeError(message);
+ }
+ }
+ return ret;
+ }
+
// ES6: 7.3.3 Set (O, P, V, Throw)
- inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow)
+ inline bool set(StringOrSymbol *name, const Value &v, ThrowOnFailure shouldThrow)
{
- bool ret = vtable()->put(this, name, v);
+ bool ret = vtable()->put(this, name->toPropertyKey(), v, this);
// ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
ExecutionEngine *e = engine();
@@ -392,42 +351,37 @@ public:
return ret;
}
- PropertyAttributes query(String *name) const
- { return vtable()->query(this, name); }
- PropertyAttributes queryIndexed(uint index) const
- { return vtable()->queryIndexed(this, index); }
- bool deleteProperty(String *name)
- { return vtable()->deleteProperty(this, name); }
- bool deleteIndexedProperty(uint index)
- { return vtable()->deleteIndexedProperty(this, index); }
+ bool deleteProperty(PropertyKey id)
+ { return vtable()->deleteProperty(this, id); }
void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{ vtable()->advanceIterator(this, it, name, index, p, attributes); }
- uint getLength() const { return vtable()->getLength(this); }
+ qint64 getLength() const { return vtable()->getLength(this); }
ReturnedValue instanceOf(const Value &var) const
{ return vtable()->instanceOf(this, var); }
protected:
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static bool putIndexed(Managed *m, uint index, const Value &value);
- static PropertyAttributes query(const Managed *m, String *name);
- static PropertyAttributes queryIndexed(const Managed *m, uint index);
- static bool deleteProperty(Managed *m, String *name);
- static bool deleteIndexedProperty(Managed *m, uint index);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- static uint getLength(const Managed *m);
- static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ 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 bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
+ static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+ static bool virtualIsExtensible(const Managed *m);
+ static bool virtualPreventExtensions(Managed *);
+ static Heap::Object *virtualGetPrototypeOf(const Managed *);
+ static bool virtualSetPrototypeOf(Managed *, const Object *);
+ static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static qint64 virtualGetLength(const Managed *m);
+ static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
private:
- ReturnedValue internalGet(String *name, bool *hasProperty) const;
- ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const;
- bool internalPut(String *name, const Value &value);
- bool internalPutIndexed(uint index, const Value &value);
- bool internalDeleteProperty(String *name);
- bool internalDeleteIndexedProperty(uint index);
+ bool internalDefineOwnProperty(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs);
+ ReturnedValue internalGet(StringOrSymbol *name, const Value *receiver, bool *hasProperty) const;
+ ReturnedValue internalGetIndexed(uint index, const Value *receiver, bool *hasProperty) const;
+ bool internalPut(PropertyKey id, const Value &value, Value *receiver);
+ bool internalDeleteProperty(PropertyKey id);
friend struct ObjectIterator;
friend struct ObjectPrototype;
@@ -499,10 +453,12 @@ struct ArrayObject: Object {
void init(ExecutionEngine *engine);
- using Object::getLength;
- static uint getLength(const Managed *m);
+ static qint64 virtualGetLength(const Managed *m);
QStringList toQStringList() const;
+protected:
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+
};
inline void Object::setArrayLengthUnchecked(uint l)
@@ -551,7 +507,7 @@ inline void Object::arraySet(uint index, const Value &value)
template<>
inline const ArrayObject *Value::as() const {
- return isManaged() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
}
#ifndef V4_BOOTSTRAP
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 7bf7e1aa04..73c09a864a 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -42,9 +42,29 @@
#include "qv4identifier_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4string_p.h"
+#include "qv4iterator_p.h"
using namespace QV4;
+void ForInIteratorPrototype::init(ExecutionEngine *)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+}
+
+ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ const ForInIteratorObject *forIn = thisObject->as<ForInIteratorObject>();
+ Q_ASSERT(forIn);
+ Scope scope(b->engine());
+ ScopedValue n(scope, forIn->nextPropertyName());
+ bool done = false;
+ if (n->asReturnedValue() == Encode::null()) {
+ done = true;
+ n = Primitive::undefinedValue();
+ }
+ return IteratorPrototype::createIterResultObject(scope.engine, n, done);
+}
+
void ObjectIterator::init(const Object *o)
{
object->setM(o ? o->m() : nullptr);
@@ -84,12 +104,12 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib
n = *name;
bool shadowed = false;
while (o->d() != current->heapObject()) {
- if ((!!n && o->hasOwnProperty(n)) ||
- (*index != UINT_MAX && o->hasOwnProperty(*index))) {
+ PropertyKey id = n ? (n->toPropertyKey()) : PropertyKey::fromArrayIndex(*index);
+ if (id.isValid() && o->getOwnProperty(id) != Attr_Invalid) {
shadowed = true;
break;
}
- o = o->prototype();
+ o = o->getPrototypeOf();
}
if (shadowed)
continue;
@@ -98,7 +118,7 @@ void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttrib
}
if (flags & WithProtoChain)
- current->setM(co->prototype());
+ current->setM(co->getPrototypeOf());
else
current->setM(nullptr);
@@ -175,11 +195,11 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString()
}
-DEFINE_OBJECT_VTABLE(ForEachIteratorObject);
+DEFINE_OBJECT_VTABLE(ForInIteratorObject);
-void Heap::ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
+void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
{
- ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that);
+ ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that);
o->workArea[0].mark(markStack);
o->workArea[1].mark(markStack);
Object::markObjects(that, markStack);
diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h
index 744d16301a..1e7000ad1f 100644
--- a/src/qml/jsruntime/qv4objectiterator_p.h
+++ b/src/qml/jsruntime/qv4objectiterator_p.h
@@ -92,8 +92,8 @@ struct Q_QML_EXPORT ObjectIterator: ObjectIteratorData
ObjectIterator(Scope &scope, const Object *o, uint flags)
{
engine = scope.engine;
- object = scope.alloc(1);
- current = scope.alloc(1);
+ object = scope.alloc();
+ current = scope.alloc();
arrayNode = nullptr;
arrayIndex = 0;
memberIndex = 0;
@@ -111,7 +111,7 @@ private:
};
namespace Heap {
-struct ForEachIteratorObject : Object {
+struct ForInIteratorObject : Object {
void init(QV4::Object *o);
ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); }
Value workArea[2];
@@ -123,15 +123,24 @@ private:
}
-struct ForEachIteratorObject: Object {
- V4_OBJECT2(ForEachIteratorObject, Object)
- Q_MANAGED_TYPE(ForeachIteratorObject)
+struct ForInIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ForInIteratorObject: Object {
+ V4_OBJECT2(ForInIteratorObject, Object)
+ Q_MANAGED_TYPE(ForInIterator)
+ V4_PROTOTYPE(forInIteratorPrototype)
- ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); }
+ ReturnedValue nextPropertyName() const { return d()->it().nextPropertyNameAsString(); }
};
inline
-void Heap::ForEachIteratorObject::init(QV4::Object *o)
+void Heap::ForInIteratorObject::init(QV4::Object *o)
{
Object::init();
it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o,
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index b998b78520..65e9b836d1 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -47,6 +47,8 @@
#include "qv4objectiterator_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
+#include "qv4propertykey_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
@@ -61,23 +63,23 @@ void Heap::ObjectCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Object"));
}
-ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ObjectCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
ExecutionEngine *v4 = f->engine();
- const ObjectCtor *ctor = static_cast<const ObjectCtor *>(f);
+ const ObjectCtor *nt = static_cast<const ObjectCtor *>(newTarget);
if (!argc || argv[0].isUndefined() || argv[0].isNull()) {
Scope scope(v4);
ScopedObject obj(scope, scope.engine->newObject());
- ScopedObject proto(scope, ctor->get(scope.engine->id_prototype()));
+ ScopedObject proto(scope, nt->get(scope.engine->id_prototype()));
if (!!proto)
- obj->setPrototype(proto);
+ obj->setPrototypeOf(proto);
return obj.asReturnedValue();
} else {
return argv[0].toObject(v4)->asReturnedValue();
}
}
-ReturnedValue ObjectCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc)
+ReturnedValue ObjectCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc)
{
ExecutionEngine *v4 = m->engine();
if (!argc || argv[0].isUndefined() || argv[0].isNull()) {
@@ -97,6 +99,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1);
ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2);
ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1);
+ ctor->defineDefaultProperty(QStringLiteral("getOwnPropertySymbols"), method_getOwnPropertySymbols, 1);
ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2);
ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2);
ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3);
@@ -104,10 +107,12 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
ctor->defineDefaultProperty(QStringLiteral("seal"), method_seal, 1);
ctor->defineDefaultProperty(QStringLiteral("freeze"), method_freeze, 1);
ctor->defineDefaultProperty(QStringLiteral("preventExtensions"), method_preventExtensions, 1);
+ ctor->defineDefaultProperty(QStringLiteral("is"), method_is, 2);
ctor->defineDefaultProperty(QStringLiteral("isSealed"), method_isSealed, 1);
ctor->defineDefaultProperty(QStringLiteral("isFrozen"), method_isFrozen, 1);
ctor->defineDefaultProperty(QStringLiteral("isExtensible"), method_isExtensible, 1);
ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1);
+ ctor->defineDefaultProperty(QStringLiteral("setPrototypeOf"), method_setPrototypeOf, 2);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(v4->id_toString(), method_toString, 0);
@@ -119,11 +124,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
defineDefaultProperty(QStringLiteral("__defineGetter__"), method_defineGetter, 2);
defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2);
- ExecutionContext *global = v4->rootContext();
- ScopedProperty p(scope);
- p->value = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_get_proto);
- p->set = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_set_proto);
- insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable);
+ defineAccessorProperty(v4->id___proto__(), method_get_proto, method_set_proto);
}
ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc)
@@ -136,10 +137,19 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co
if (scope.engine->hasException)
return QV4::Encode::undefined();
- ScopedObject p(scope, o->prototype());
+ ScopedObject p(scope, o->getPrototypeOf());
return (!!p ? p->asReturnedValue() : Encode::null());
}
+ReturnedValue ObjectPrototype::method_is(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ if (!argc)
+ return Encode(true);
+ if (argc == 1)
+ return Encode((argv[0].isUndefined() ? true : false));
+ return Encode(argv[0].sameValue(argv[1]));
+}
+
ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
Scope scope(b);
@@ -154,13 +164,12 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj
static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate();
ScopedValue v(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
- ScopedString name(scope, v->toString(scope.engine));
+ ScopedPropertyKey name(scope, v->toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- PropertyAttributes attrs;
ScopedProperty desc(scope);
- O->getOwnProperty(name, &attrs, desc);
+ PropertyAttributes attrs = O->getOwnProperty(name, desc);
return fromPropertyDescriptor(scope.engine, desc, attrs);
}
@@ -177,6 +186,28 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *
return Encode(getOwnPropertyNames(scope.engine, argv[0]));
}
+ReturnedValue ObjectPrototype::method_getOwnPropertySymbols(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0].toObject(scope.engine));
+ if (!O)
+ return Encode::undefined();
+ Heap::InternalClass *ic = O->d()->internalClass;
+ ScopedValue n(scope);
+ ScopedArrayObject array(scope, scope.engine->newArrayObject());
+ for (uint i = 0; i < ic->size; ++i) {
+ PropertyKey id = ic->nameMap.at(i);
+ n = id.asStringOrSymbol();
+ if (!n || !n->isSymbol())
+ continue;
+ array->push_back(n);
+ }
+ return array->asReturnedValue();
+}
+
// 19.1.2.1
ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
@@ -204,11 +235,10 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu
ScopedString nextKey(scope);
ScopedValue propValue(scope);
for (quint32 i = 0; i < length; ++i) {
- nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine);
+ nextKey = Value::fromReturnedValue(keys->get(i)).toString(scope.engine);
- PropertyAttributes attrs;
ScopedProperty prop(scope);
- from->getOwnProperty(nextKey, &attrs, prop);
+ PropertyAttributes attrs = from->getOwnProperty(nextKey->toPropertyKey(), prop);
if (attrs == PropertyFlag::Attr_Invalid)
continue;
@@ -235,7 +265,7 @@ ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, cons
ScopedObject O(scope, argv[0]);
ScopedObject newObject(scope, scope.engine->newObject());
- newObject->setPrototype(O);
+ newObject->setPrototypeOf(O);
if (argc > 1 && !argv[1].isUndefined()) {
@@ -255,7 +285,7 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co
return scope.engine->throwTypeError();
ScopedObject O(scope, argv[0]);
- ScopedString name(scope, argc > 1 ? argv[1] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
@@ -266,7 +296,7 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co
if (scope.engine->hasException)
return QV4::Encode::undefined();
- if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs))
+ if (!O->defineOwnProperty(name, pd, attrs))
THROW_TYPE_ERROR();
return O.asReturnedValue();
@@ -287,7 +317,7 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b,
ScopedValue val(scope);
ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
- ScopedString name(scope);
+ ScopedStringOrSymbol name(scope);
ScopedProperty pd(scope);
ScopedProperty n(scope);
while (1) {
@@ -303,9 +333,9 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b,
return QV4::Encode::undefined();
bool ok;
if (name)
- ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs);
+ ok = O->defineOwnProperty(name->toPropertyKey(), n, nattrs);
else
- ok = O->__defineOwnProperty__(scope.engine, index, n, nattrs);
+ ok = O->defineOwnProperty(PropertyKey::fromArrayIndex(index), n, nattrs);
if (!ok)
THROW_TYPE_ERROR();
}
@@ -372,7 +402,7 @@ ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b,
if (!o)
return argv[0].asReturnedValue();
- o->setInternalClass(o->internalClass()->nonExtensible());
+ o->preventExtensions();
return o.asReturnedValue();
}
@@ -477,19 +507,53 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value
return a.asReturnedValue();
}
+ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f->engine());
+ if (argc < 2 || argv[0].isNullOrUndefined() || !(argv[1].isObject() || argv[1].isNull()))
+ return scope.engine->throwTypeError();
+
+ if (!argv[0].isObject())
+ return argv[0].asReturnedValue();
+
+ ScopedObject o(scope, argv[0]);
+ const Object *p = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1);
+ bool ok = o->setPrototypeOf(p);
+ if (!ok)
+ return scope.engine->throwTypeError(QStringLiteral("Could not change prototype."));
+ return o->asReturnedValue();
+}
+
ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
+ QString string;
if (thisObject->isUndefined()) {
- return Encode(v4->newString(QStringLiteral("[object Undefined]")));
+ string = QStringLiteral("[object Undefined]");
} else if (thisObject->isNull()) {
- return Encode(v4->newString(QStringLiteral("[object Null]")));
+ string = QStringLiteral("[object Null]");
} else {
+ const Object *o = thisObject->as<Object>();
+ if (!o) {
+ // primitive, get the proper prototype
+ if (thisObject->isBoolean())
+ o = v4->booleanPrototype();
+ else if (thisObject->isNumber())
+ o = v4->numberPrototype();
+ else if (thisObject->isString())
+ o = v4->stringPrototype();
+ else if (thisObject->isSymbol())
+ o = v4->symbolPrototype();
+ Q_ASSERT(o);
+ }
+ QString name = o->className();
Scope scope(v4);
- ScopedObject obj(scope, thisObject->toObject(scope.engine));
- QString className = obj->className();
- return Encode(v4->newString(QStringLiteral("[object %1]").arg(className)));
+ ScopedString toStringTag(scope, o->get(v4->symbol_toStringTag()));
+ if (toStringTag)
+ name = toStringTag->toQString();
+ string = QStringLiteral("[object %1]").arg(name);
}
+ return Encode(v4->newString(string));
}
ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -514,15 +578,13 @@ ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Val
ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- ScopedString P(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedPropertyKey P(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
ScopedObject O(scope, thisObject->toObject(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- bool r = O->hasOwnProperty(P);
- if (!r)
- r = !O->query(P).isEmpty();
+ bool r = O->getOwnProperty(P) != Attr_Invalid;
return Encode(r);
}
@@ -536,11 +598,11 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con
ScopedObject O(scope, thisObject->toObject(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- ScopedObject proto(scope, V->prototype());
+ ScopedObject proto(scope, V->getPrototypeOf());
while (proto) {
if (O->d() == proto->d())
return Encode(true);
- proto = proto->prototype();
+ proto = proto->getPrototypeOf();
}
return Encode(false);
}
@@ -548,15 +610,14 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con
ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- ScopedString p(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedPropertyKey p(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
ScopedObject o(scope, thisObject->toObject(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- PropertyAttributes attrs;
- o->getOwnProperty(p, &attrs);
+ PropertyAttributes attrs = o->getOwnProperty(p);
return Encode(attrs.isEnumerable());
}
@@ -584,7 +645,7 @@ ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, cons
ScopedProperty pd(scope);
pd->value = f;
pd->set = Primitive::emptyValue();
- bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor);
if (!ok)
THROW_TYPE_ERROR();
RETURN_UNDEFINED();
@@ -614,7 +675,7 @@ ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, cons
ScopedProperty pd(scope);
pd->value = Primitive::emptyValue();
pd->set = f;
- bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor);
if (!ok)
THROW_TYPE_ERROR();
RETURN_UNDEFINED();
@@ -627,32 +688,21 @@ ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const V
if (!o)
THROW_TYPE_ERROR();
- return Encode(o->prototype());
+ return Encode(o->getPrototypeOf());
}
ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
ScopedObject o(scope, thisObject);
- if (!o || !argc)
+ if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull()))
THROW_TYPE_ERROR();
- if (argv[0].isNull()) {
- o->setPrototype(nullptr);
- RETURN_UNDEFINED();
- }
-
- ScopedObject p(scope, argv[0]);
- bool ok = false;
- if (!!p) {
- if (o->prototype() == p->d()) {
- ok = true;
- } else if (o->isExtensible()) {
- ok = o->setPrototype(p);
- }
- }
+ const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv);
+ bool ok = o->setPrototypeOf(p);
if (!ok)
- return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
+ return scope.engine->throwTypeError(QStringLiteral("Could not change prototype."));
+ return Encode::undefined();
RETURN_UNDEFINED();
}
@@ -670,13 +720,13 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
desc->set = Primitive::emptyValue();
ScopedValue tmp(scope);
- if (o->hasProperty(engine->id_enumerable()))
+ if (o->hasProperty(engine->id_enumerable()->toPropertyKey()))
attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean());
- if (o->hasProperty(engine->id_configurable()))
+ if (o->hasProperty(engine->id_configurable()->toPropertyKey()))
attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean());
- if (o->hasProperty(engine->id_get())) {
+ if (o->hasProperty(engine->id_get()->toPropertyKey())) {
ScopedValue get(scope, o->get(engine->id_get()));
FunctionObject *f = get->as<FunctionObject>();
if (f || get->isUndefined()) {
@@ -688,7 +738,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
attrs->setType(PropertyAttributes::Accessor);
}
- if (o->hasProperty(engine->id_set())) {
+ if (o->hasProperty(engine->id_set()->toPropertyKey())) {
ScopedValue set(scope, o->get(engine->id_set()));
FunctionObject *f = set->as<FunctionObject>();
if (f || set->isUndefined()) {
@@ -700,7 +750,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
attrs->setType(PropertyAttributes::Accessor);
}
- if (o->hasProperty(engine->id_writable())) {
+ if (o->hasProperty(engine->id_writable()->toPropertyKey())) {
if (attrs->isAccessor()) {
engine->throwTypeError();
return;
@@ -710,7 +760,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
desc->value = Primitive::undefinedValue();
}
- if (o->hasProperty(engine->id_value())) {
+ if (o->hasProperty(engine->id_value()->toPropertyKey())) {
if (attrs->isAccessor()) {
engine->throwTypeError();
return;
@@ -734,29 +784,28 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c
// is the standard built-in constructor with that name.
ScopedObject o(scope, engine->newObject());
ScopedString s(scope);
+ ScopedValue v(scope);
- ScopedProperty pd(scope);
if (attrs.isData()) {
- pd->value = desc->value;
s = engine->newString(QStringLiteral("value"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = Primitive::fromBoolean(attrs.isWritable());
+ o->put(s, desc->value);
+ v = Primitive::fromBoolean(attrs.isWritable());
s = engine->newString(QStringLiteral("writable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
} else {
- pd->value = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined();
+ v = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined();
s = engine->newString(QStringLiteral("get"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined();
+ o->put(s, v);
+ v = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined();
s = engine->newString(QStringLiteral("set"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
}
- pd->value = Primitive::fromBoolean(attrs.isEnumerable());
+ v = Primitive::fromBoolean(attrs.isEnumerable());
s = engine->newString(QStringLiteral("enumerable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = Primitive::fromBoolean(attrs.isConfigurable());
+ o->put(s, v);
+ v = Primitive::fromBoolean(attrs.isConfigurable());
s = engine->newString(QStringLiteral("configurable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
return o.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h
index 2b231d46ad..0314c05766 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -70,28 +70,31 @@ struct ObjectCtor: FunctionObject
{
V4_OBJECT2(ObjectCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc);
};
struct ObjectPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
- static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_assign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_create(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_defineProperties(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_freeze(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertySymbols(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_is(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp
index 7fc74173e3..f8bc28160e 100644
--- a/src/qml/jsruntime/qv4persistent.cpp
+++ b/src/qml/jsruntime/qv4persistent.cpp
@@ -98,9 +98,9 @@ Page *allocatePage(PersistentValueStorage *storage)
p->header.freeList = 0;
insertInFront(storage, p);
for (int i = 0; i < kEntriesPerPage - 1; ++i) {
- p->values[i].setEmpty(i + 1);
+ p->values[i] = Encode(i + 1);
}
- p->values[kEntriesPerPage - 1].setEmpty(-1);
+ p->values[kEntriesPerPage - 1] = Encode(-1);
return p;
}
@@ -226,7 +226,7 @@ void PersistentValueStorage::free(Value *v)
Page *p = getPage(v);
- v->setEmpty(p->header.freeList);
+ *v = Encode(p->header.freeList);
p->header.freeList = v - p->values;
if (!--p->header.refCount)
freePage(p);
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index 5fd200efc1..b337243204 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -78,7 +78,7 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine(
void Profiler::stopProfiling()
{
featuresEnabled = 0;
- reportData(true);
+ reportData();
m_sentLocations.clear();
}
@@ -89,7 +89,7 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2)
(call1.m_end == call2.m_end && call1.m_function < call2.m_function)));
}
-void Profiler::reportData(bool trackLocations)
+void Profiler::reportData()
{
std::sort(m_data.begin(), m_data.end());
QVector<FunctionCallProperties> properties;
@@ -100,12 +100,11 @@ void Profiler::reportData(bool trackLocations)
properties.append(call.properties());
Function *function = call.function();
SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)];
- if (!trackLocations || !marker.isValid()) {
+ if (!marker.isValid()) {
FunctionLocation &location = locations[properties.constLast().id];
if (!location.isValid())
location = call.resolveLocation();
- if (trackLocations)
- marker.setFunction(function);
+ marker.setFunction(function);
}
}
diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h
index e8c154e4e7..8461384e9a 100644
--- a/src/qml/jsruntime/qv4profiling_p.h
+++ b/src/qml/jsruntime/qv4profiling_p.h
@@ -251,7 +251,7 @@ public:
void stopProfiling();
void startProfiling(quint64 features);
- void reportData(bool trackLocations);
+ void reportData();
void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
signals:
diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h
index 7cb106c424..26dc7a83c3 100644
--- a/src/qml/jsruntime/qv4property_p.h
+++ b/src/qml/jsruntime/qv4property_p.h
@@ -142,6 +142,19 @@ inline void Property::merge(PropertyAttributes &attrs, const Property *other, Pr
}
}
+struct PropertyIndex {
+ Heap::Base *base;
+ Value *slot;
+
+ void set(EngineBase *e, Value newVal) {
+ WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue());
+ }
+ const Value *operator->() const { return slot; }
+ const Value &operator*() const { return *slot; }
+ bool isNull() const { return !slot; }
+};
+
+
}
Q_DECLARE_TYPEINFO(QV4::Property, Q_MOVABLE_TYPE);
diff --git a/src/qml/jsruntime/qv4propertykey.cpp b/src/qml/jsruntime/qv4propertykey.cpp
new file mode 100644
index 0000000000..e5e96bedb8
--- /dev/null
+++ b/src/qml/jsruntime/qv4propertykey.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4propertykey_p.h"
+
+#include <QtCore/qstring.h>
+#include <qv4string_p.h>
+
+QV4::Heap::StringOrSymbol *QV4::PropertyKey::toStringOrSymbol(QV4::ExecutionEngine *e)
+{
+ if (isArrayIndex())
+ return Primitive::fromUInt32(asArrayIndex()).toString(e);
+ return static_cast<Heap::StringOrSymbol *>(asStringOrSymbol());
+}
+
+bool QV4::PropertyKey::isString() const {
+ Heap::StringOrSymbol *s = asStringOrSymbol();
+ return s && s->internalClass->vtable->isString;
+}
+
+bool QV4::PropertyKey::isSymbol() const {
+ Heap::Base *s = asStringOrSymbol();
+ return s && !s->internalClass->vtable->isString && s->internalClass->vtable->isStringOrSymbol;
+}
+
+QString QV4::PropertyKey::toQString() const
+{
+ if (isArrayIndex())
+ return QString::number(asArrayIndex());
+ Heap::Base *b = asStringOrSymbol();
+ Q_ASSERT(b->internalClass->vtable->isStringOrSymbol);
+ Heap::StringOrSymbol *s = static_cast<Heap::StringOrSymbol *>(b);
+ return s->toQString();
+}
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
new file mode 100644
index 0000000000..00bf8fb195
--- /dev/null
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4PROPERTYKEY_H
+#define QV4PROPERTYKEY_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QString;
+
+namespace QV4 {
+
+struct PropertyKey
+{
+private:
+ // Property keys are Strings, Symbols or unsigned integers.
+ // For convenience we derive them from Values, allowing us to store them
+ // on the JS stack
+ //
+ // They do however behave somewhat different than a Value:
+ // * If the key is a String, the pointer to the string is stored in the identifier
+ // table and thus unique.
+ // * If the key is a Symbol it simply points to the referenced symbol object
+ // * if the key is an array index (a uint < UINT_MAX), it's encoded as an
+ // integer value
+ quint64 val;
+
+ // Important: Always keep this in sync with the definitions for Integers and heap objects in Value
+ static const quint64 ArrayIndexMask = 0x3800000000000ull;
+ enum {
+ IsManagedOrUndefined_Shift = 64-15,
+ };
+ inline bool isManaged() const { return (val >> IsManagedOrUndefined_Shift) == 0; }
+ inline quint32 value() const { return val & quint64(~quint32(0)); }
+
+#if QT_POINTER_SIZE == 8
+ QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const
+ {
+ Heap::StringOrSymbol *b;
+ memcpy(&b, &val, 8);
+ return b;
+ }
+ QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b)
+ {
+ memcpy(&val, &b, 8);
+ }
+#elif QT_POINTER_SIZE == 4
+ QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const
+ {
+ Q_STATIC_ASSERT(sizeof(Heap::StringOrSymbol*) == sizeof(quint32));
+ Heap::StringOrSymbol *b;
+ quint32 v = value();
+ memcpy(&b, &v, 4);
+ return b;
+ }
+ QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b)
+ {
+ quint32 v;
+ memcpy(&v, &b, 4);
+ val = v;
+ }
+#endif
+
+public:
+ static PropertyKey invalid() { PropertyKey key; key.val = 0; return key; }
+ static PropertyKey fromArrayIndex(uint idx) { PropertyKey key; key.val = ArrayIndexMask | static_cast<quint64>(idx); return key; }
+ bool isStringOrSymbol() const { return isManaged() && val != 0; }
+ uint asArrayIndex() const { return (isManaged() || val == 0) ? std::numeric_limits<uint>::max() : static_cast<uint>(val & 0xffffffff); }
+ uint isArrayIndex() const { return !isManaged() && val != 0; }
+ bool isValid() const { return val != 0; }
+ static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b)
+ { PropertyKey key; key.setM(b); return key; }
+ Heap::StringOrSymbol *asStringOrSymbol() const {
+ if (!isManaged())
+ return nullptr;
+ return m();
+ }
+
+ bool isString() const;
+ bool isSymbol() const;
+
+ Q_QML_EXPORT QString toQString() const;
+ Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e);
+ quint64 id() const { return val; }
+
+ bool operator ==(const PropertyKey &other) const { return val == other.val; }
+ bool operator !=(const PropertyKey &other) const { return val != other.val; }
+ bool operator <(const PropertyKey &other) const { return val < other.val; }
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp
new file mode 100644
index 0000000000..794aee8c24
--- /dev/null
+++ b/src/qml/jsruntime/qv4proxy.cpp
@@ -0,0 +1,553 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qv4proxy_p.h"
+#include "qv4symbol_p.h"
+#include "qv4jscall_p.h"
+#include "qv4objectproto_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(ProxyObject);
+
+void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler)
+{
+ Object::init();
+ ExecutionEngine *e = internalClass->engine;
+ this->target.set(e, target->d());
+ this->handler.set(e, handler->d());
+}
+
+ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedValue trap(scope, handler->get(scope.engine->id_get()));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->get(id, receiver, hasProperty);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+ if (hasProperty)
+ *hasProperty = true;
+
+ JSCallData cdata(scope, 3, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.toStringOrSymbol(scope.engine);
+ cdata.args[2] = *receiver;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
+ if (attributes.isData() && !attributes.isWritable()) {
+ if (!trapResult->sameValue(targetDesc->value))
+ return scope.engine->throwTypeError();
+ }
+ if (attributes.isAccessor() && targetDesc->value.isUndefined()) {
+ if (!trapResult->isUndefined())
+ return scope.engine->throwTypeError();
+ }
+ }
+ return trapResult->asReturnedValue();
+}
+
+bool ProxyObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedValue trap(scope, handler->get(scope.engine->id_set()));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->put(id, value, receiver);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 4, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.toStringOrSymbol(scope.engine);
+ cdata.args[2] = value;
+ cdata.args[3] = *receiver;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->toBoolean())
+ return false;
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
+ if (attributes.isData() && !attributes.isWritable()) {
+ if (!value.sameValue(targetDesc->value))
+ return scope.engine->throwTypeError();
+ }
+ if (attributes.isAccessor() && targetDesc->set.isUndefined())
+ return scope.engine->throwTypeError();
+ }
+ return true;
+}
+
+bool ProxyObject::virtualDeleteProperty(Managed *m, PropertyKey id)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("deleteProperty")));
+ ScopedValue trap(scope, handler->get(deleteProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->deleteProperty(id);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 3, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.toStringOrSymbol(scope.engine);
+ cdata.args[2] = o->d(); // ### fix receiver handling
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->toBoolean())
+ return false;
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes == Attr_Invalid)
+ return true;
+ if (!attributes.isConfigurable())
+ return scope.engine->throwTypeError();
+ return true;
+}
+
+bool ProxyObject::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("has")));
+ ScopedValue trap(scope, handler->get(hasProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->hasProperty(id);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 2, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (!result) {
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes != Attr_Invalid) {
+ if (!attributes.isConfigurable() || !target->isExtensible())
+ return scope.engine->throwTypeError();
+ }
+ }
+ return result;
+}
+
+PropertyAttributes ProxyObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("getOwnPropertyDescriptor")));
+ ScopedValue trap(scope, handler->get(deleteProp));
+ if (scope.hasException())
+ return Attr_Invalid;
+ if (trap->isNullOrUndefined())
+ return target->getOwnProperty(id, p);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+
+ JSCallData cdata(scope, 2, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->isObject() && !trapResult->isUndefined()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc);
+ if (trapResult->isUndefined()) {
+ p->value = Encode::undefined();
+ if (targetAttributes == Attr_Invalid) {
+ p->value = Encode::undefined();
+ return Attr_Invalid;
+ }
+ if (!targetAttributes.isConfigurable() || !target->isExtensible()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+ return Attr_Invalid;
+ }
+
+ //bool extensibleTarget = target->isExtensible();
+ ScopedProperty resultDesc(scope);
+ PropertyAttributes resultAttributes;
+ ObjectPrototype::toPropertyDescriptor(scope.engine, trapResult, resultDesc, &resultAttributes);
+ resultDesc->fullyPopulated(&resultAttributes);
+
+ // ###
+ //Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
+ //If valid is false, throw a TypeError exception.
+
+ if (!resultAttributes.isConfigurable()) {
+ if (targetAttributes == Attr_Invalid || !targetAttributes.isConfigurable()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+ }
+
+ p->value = resultDesc->value;
+ p->set = resultDesc->set;
+ return resultAttributes;
+}
+
+bool ProxyObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString prop(scope, scope.engine->newString(QStringLiteral("defineProperty")));
+ ScopedValue trap(scope, handler->get(prop));
+ if (scope.hasException())
+ return false;
+ if (trap->isNullOrUndefined())
+ return target->defineOwnProperty(id, p, attrs);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ JSCallData cdata(scope, 3, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.isArrayIndex() ? Primitive::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
+ cdata.args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs);
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (!result)
+ return false;
+
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc);
+ bool extensibleTarget = target->isExtensible();
+ bool settingConfigFalse = attrs.hasConfigurable() && !attrs.isConfigurable();
+ if (targetAttributes == Attr_Invalid) {
+ if (!extensibleTarget || settingConfigFalse) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ } else {
+ // ###
+ // if IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false throw a type error.
+ if (settingConfigFalse && targetAttributes.isConfigurable()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ProxyObject::virtualIsExtensible(const Managed *m)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("isExtensible")));
+ ScopedValue trap(scope, handler->get(hasProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->isExtensible();
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (result != target->isExtensible()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ return result;
+}
+
+bool ProxyObject::virtualPreventExtensions(Managed *m)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("preventExtensions")));
+ ScopedValue trap(scope, handler->get(hasProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->preventExtensions();
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (result && target->isExtensible()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ return result;
+}
+
+Heap::Object *ProxyObject::virtualGetPrototypeOf(const Managed *m)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("getPrototypeOf")));
+ ScopedValue trap(scope, handler->get(name));
+ if (scope.hasException())
+ return nullptr;
+ if (trap->isNullOrUndefined())
+ return target->getPrototypeOf();
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->isNull() && !trapResult->isObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ Heap::Object *proto = trapResult->isNull() ? nullptr : static_cast<Heap::Object *>(trapResult->heapObject());
+ if (!target->isExtensible()) {
+ Heap::Object *targetProto = target->getPrototypeOf();
+ if (proto != targetProto) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ }
+ return proto;
+}
+
+bool ProxyObject::virtualSetPrototypeOf(Managed *m, const Object *p)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("setPrototypeOf")));
+ ScopedValue trap(scope, handler->get(name));
+ if (scope.hasException())
+ return false;
+ if (trap->isNullOrUndefined())
+ return target->setPrototypeOf(p);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ JSCallData cdata(scope, 2, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = p ? p->asReturnedValue() : Encode::null();
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (!result)
+ return false;
+ if (!target->isExtensible()) {
+ Heap::Object *targetProto = target->getPrototypeOf();
+ if (p->d() != targetProto) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ }
+ return true;
+}
+
+//ReturnedValue ProxyObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+//{
+
+//}
+
+//ReturnedValue ProxyObject::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+//{
+
+//}
+
+DEFINE_OBJECT_VTABLE(Proxy);
+
+void Heap::Proxy::init(QV4::ExecutionContext *ctx)
+{
+ Heap::FunctionObject::init(ctx, QStringLiteral("Proxy"));
+
+ Scope scope(ctx);
+ Scoped<QV4::Proxy> ctor(scope, this);
+ ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2);
+ ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Primitive::fromInt32(2));
+}
+
+ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ Scope scope(f);
+ if (argc < 2 || !argv[0].isObject() || !argv[1].isObject())
+ return scope.engine->throwTypeError();
+
+ const Object *target = static_cast<const Object *>(argv);
+ const Object *handler = static_cast<const Object *>(argv + 1);
+ if (const ProxyObject *ptarget = target->as<ProxyObject>())
+ if (!ptarget->d()->handler)
+ return scope.engine->throwTypeError();
+ if (const ProxyObject *phandler = handler->as<ProxyObject>())
+ if (!phandler->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, scope.engine->memoryManager->allocate<ProxyObject>(target, handler));
+ return o->asReturnedValue();
+}
+
+ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedObject proxy(scope, Proxy::virtualCallAsConstructor(f, argv, argc, f));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke")));
+ ScopedFunctionObject revoker(scope, createBuiltinFunction(scope.engine, revoke, method_revoke, 0));
+ revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy);
+
+ ScopedObject o(scope, scope.engine->newObject());
+ ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy")));
+ o->defineDefaultProperty(p, proxy);
+ o->defineDefaultProperty(revoke, revoker);
+ return o->asReturnedValue();
+}
+
+ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<ProxyObject> proxy(scope, f->get(scope.engine->symbol_revokableProxy()));
+ Q_ASSERT(proxy);
+
+ proxy->d()->target.set(scope.engine, nullptr);
+ proxy->d()->handler.set(scope.engine, nullptr);
+ return Encode::undefined();
+}
diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h
new file mode 100644
index 0000000000..4d631e882c
--- /dev/null
+++ b/src/qml/jsruntime/qv4proxy_p.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4PROXY_P_H
+#define QV4PROXY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define ProxyObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, target) \
+ Member(class, Pointer, Object *, handler)
+
+DECLARE_HEAP_OBJECT(ProxyObject, Object) {
+ DECLARE_MARKOBJECTS(ProxyObject)
+
+ void init(const QV4::Object *target, const QV4::Object *handler);
+};
+
+#define ProxyMembers(class, Member) \
+ Member(class, Pointer, Symbol *, revokableProxySymbol) \
+
+DECLARE_HEAP_OBJECT(Proxy, FunctionObject) {
+ DECLARE_MARKOBJECTS(Proxy)
+
+ void init(QV4::ExecutionContext *ctx);
+};
+
+}
+
+struct ProxyObject: Object {
+ V4_OBJECT2(ProxyObject, Object)
+ Q_MANAGED_TYPE(ProxyObject)
+ V4_INTERNALCLASS(ProxyObject)
+
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
+ static bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
+ static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+ static bool virtualIsExtensible(const Managed *m);
+ static bool virtualPreventExtensions(Managed *);
+ static Heap::Object *virtualGetPrototypeOf(const Managed *);
+ static bool virtualSetPrototypeOf(Managed *, const Object *);
+
+ // those might require a second proxy object that derives from FunctionObject...
+// static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+// static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct Proxy : FunctionObject
+{
+ V4_OBJECT2(Proxy, FunctionObject)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_revocable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_revoke(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 040f060476..dc69016559 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -78,30 +78,25 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
-ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
+
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
- // In V8 the JS global object would come _before_ the QML global object,
- // so simulate that here.
- bool hasProp;
- QV4::ScopedValue result(scope, v4->globalObject->get(name, &hasProp));
- if (hasProp) {
- if (hasProperty)
- *hasProperty = hasProp;
- return result->asReturnedValue();
- }
-
if (resource->d()->isNullWrapper)
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
if (v4->callingQmlContext() != *resource->d()->context)
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
- result = Object::get(m, name, &hasProp);
+ bool hasProp = false;
+ ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@@ -129,6 +124,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP
QObject *scopeObject = resource->getScopeObject();
+ ScopedString name(scope, id.asStringOrSymbol());
if (context->imports && name->startsWithUpper()) {
// Search for attached properties, enums and imported scripts
QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion);
@@ -139,7 +135,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP
if (r.scriptIndex != -1) {
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
if (scripts)
- return scripts->getIndexed(r.scriptIndex);
+ return scripts->get(r.scriptIndex);
return QV4::Encode::null();
} else if (r.type.isValid()) {
return QQmlTypeWrapper::create(v4, scopeObject, r.type);
@@ -219,14 +215,27 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP
context = context->parent;
}
+ // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
+ // true if we access properties of the global object.
+ result = v4->globalObject->get(name, &hasProp);
+ if (hasProp) {
+ if (hasProperty)
+ *hasProperty = hasProp;
+ return result->asReturnedValue();
+ }
+
expressionContext->unresolvedNames = true;
return Encode::undefined();
}
-bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
+bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
+
+ if (id.isSymbol() || id.isArrayIndex())
+ return Object::virtualPut(m, id, value, receiver);
+
QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m);
ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@@ -234,20 +243,20 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
return false;
QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource);
- uint member = wrapper->internalClass()->find(name);
+ uint member = wrapper->internalClass()->find(id);
if (member < UINT_MAX)
return wrapper->putValue(member, value);
if (wrapper->d()->isNullWrapper) {
if (wrapper && wrapper->d()->readOnly) {
- QString error = QLatin1String("Invalid write to global property \"") + name->toQString() +
+ QString error = QLatin1String("Invalid write to global property \"") + id.toQString() +
QLatin1Char('"');
ScopedString e(scope, v4->newString(error));
v4->throwError(e);
return false;
}
- return Object::put(m, name, value);
+ return Object::virtualPut(m, id, value, receiver);
}
// It's possible we could delay the calculation of the "actual" context (in the case
@@ -261,6 +270,7 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
// See QV8ContextWrapper::Getter for resolution order
QObject *scopeObject = wrapper->getScopeObject();
+ ScopedString name(scope, id.asStringOrSymbol());
while (context) {
const QV4::IdentifierHash &properties = context->propertyNames();
@@ -291,7 +301,7 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
return false;
}
- return Object::put(m, name, value);
+ return Object::virtualPut(m, id, value, receiver);
}
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
@@ -312,7 +322,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons
context->isInternal = true;
context->isJSContext = true;
- Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, (QObject*)nullptr));
+ Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, (QObject*)nullptr));
qml->d()->isNullWrapper = true;
qml->setReadOnly(false);
@@ -330,7 +340,7 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData *
{
Scope scope(parent);
- Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, scopeObject));
+ Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, scopeObject));
Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml);
Q_ASSERT(c->vtable() == staticVTable());
return c;
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index 647bef7fc1..b9061a3f58 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -98,8 +98,8 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
void setReadOnly(bool b) { d()->readOnly = b; }
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
+ 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);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index d63d42478a..a17de5d94d 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -56,7 +56,11 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
+
+#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
+#endif
+
#include <private/qv4objectproto_p.h>
#include <private/qv4jsonobject_p.h>
#include <private/qv4regexpobject_p.h>
@@ -181,11 +185,13 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object
if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType()))
return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType());
} else {
+#if QT_CONFIG(qml_sequence_object)
// see if it's a sequence type
bool succeeded = false;
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded));
+ QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), !property.isWritable(), &succeeded));
if (succeeded)
return retn->asReturnedValue();
+#endif
}
if (property.propType() == QMetaType::UnknownType) {
@@ -242,7 +248,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
return QV4::QObjectMethod::create(global, object, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
- return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
+ return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
} else {
ExecutionContext *global = engine->rootContext();
return QV4::QObjectMethod::create(global, object, property->coreIndex());
@@ -308,7 +314,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
}
}
}
- return QV4::Object::get(this, name, hasProperty);
+ return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
}
QQmlData *ddata = QQmlData::get(d()->object(), false);
@@ -661,7 +667,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p
return setProperty(engine, object, property, value);
}
-bool QObjectWrapper::isEqualTo(Managed *a, Managed *b)
+bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QV4::QObjectWrapper>());
QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a);
@@ -684,60 +690,77 @@ ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
return result;
}
}
- return (engine->memoryManager->allocObject<QV4::QObjectWrapper>(object))->asReturnedValue();
+ return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue();
}
-QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty)
+QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
+ Scope scope(that);
+ ScopedString n(scope, id.asStringOrSymbol());
QQmlContextData *qmlContext = that->engine()->callingQmlContext();
- return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true);
+ return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true);
}
-bool QObjectWrapper::put(Managed *m, String *name, const Value &value)
+bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+
+ Scope scope(m);
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
- ExecutionEngine *v4 = that->engine();
+ ScopedString name(scope, id.asStringOrSymbol());
- if (v4->hasException || QQmlData::wasDeleted(that->d()->object()))
+ if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object()))
return false;
- QQmlContextData *qmlContext = v4->callingQmlContext();
- if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
+ QQmlContextData *qmlContext = scope.engine->callingQmlContext();
+ if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
QQmlData *ddata = QQmlData::get(that->d()->object());
// Types created by QML are not extensible at run-time, but for other QObjects we can store them
// as regular JavaScript properties, like on JavaScript objects.
if (ddata && ddata->context) {
QString error = QLatin1String("Cannot assign to non-existent property \"") +
name->toQString() + QLatin1Char('\"');
- v4->throwError(error);
+ scope.engine->throwError(error);
return false;
} else {
- return QV4::Object::put(m, name, value);
+ return QV4::Object::virtualPut(m, id, value, receiver);
}
}
return true;
}
-PropertyAttributes QObjectWrapper::query(const Managed *m, String *name)
+PropertyAttributes QObjectWrapper::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
{
- const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
- const QObject *thatObject = that->d()->object();
- if (QQmlData::wasDeleted(thatObject))
- return QV4::Object::query(m, name);
+ if (id.isString()) {
+ QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
+ const QObject *thatObject = that->d()->object();
+ if (!QQmlData::wasDeleted(thatObject)) {
+ Scope scope(m);
+ ScopedString n(scope, id.asStringOrSymbol());
+ QQmlContextData *qmlContext = scope.engine->callingQmlContext();
+ QQmlPropertyData local;
+ if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local)
+ || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) {
+ if (p) {
+ // ### probably not the fastest implementation
+ bool hasProperty;
+ p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true);
+ }
+ return QV4::Attr_Data;
+ }
+ }
+ }
- ExecutionEngine *engine = that->engine();
- QQmlContextData *qmlContext = engine->callingQmlContext();
- QQmlPropertyData local;
- if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local)
- || name->equals(engine->id_destroy()) || name->equals(engine->id_toString()))
- return QV4::Attr_Data;
- else
- return QV4::Object::query(m, name);
+ return QV4::Object::virtualGetOwnProperty(m, id, p);
}
-void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+void QObjectWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{
// Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
@@ -788,7 +811,7 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name
return;
}
}
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+ QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
namespace QV4 {
@@ -1619,6 +1642,7 @@ void CallArgument::initAsType(int callType)
}
}
+#if QT_CONFIG(qml_sequence_object)
template <class T, class M>
void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine)
{
@@ -1631,6 +1655,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M
}
}
}
+#endif
void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value)
{
@@ -1685,7 +1710,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
uint length = array->getLength();
for (uint ii = 0; ii < length; ++ii) {
QObject *o = nullptr;
- qobjectWrapper = array->getIndexed(ii);
+ qobjectWrapper = array->get(ii);
if (!!qobjectWrapper)
o = qobjectWrapper->object();
qlistPtr->append(o);
@@ -1713,6 +1738,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
type = callType;
} else if (callType == QMetaType::Void) {
*qvariantPtr = QVariant();
+#if QT_CONFIG(qml_sequence_object)
} else if (callType == qMetaTypeId<std::vector<int>>()
|| callType == qMetaTypeId<std::vector<qreal>>()
|| callType == qMetaTypeId<std::vector<bool>>()
@@ -1740,6 +1766,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
stdVectorQModelIndexPtr = nullptr;
fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine);
}
+#endif
} else {
queryEngine = true;
}
@@ -1833,7 +1860,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
method->d()->setObject(object);
if (QQmlData *ddata = QQmlData::get(object))
@@ -1846,7 +1873,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
method->d()->setPropertyCache(valueType->d()->propertyCache());
method->d()->index = index;
method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d());
@@ -1906,7 +1933,7 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, c
return Encode::undefined();
}
-ReturnedValue QObjectMethod::call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
{
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
return This->callInternal(thisObject, argv, argc);
@@ -2019,7 +2046,7 @@ void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
QV4::Scope scope(engine);
- Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
+ Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
mo->init(engine);
return mo->asReturnedValue();
}
@@ -2037,7 +2064,7 @@ void QMetaObjectWrapper::init(ExecutionEngine *) {
}
}
-ReturnedValue QMetaObjectWrapper::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
return This->constructInternal(argv, argc);
@@ -2068,7 +2095,7 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc)
}
Scoped<QMetaObjectWrapper> metaObject(scope, this);
object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototype(const_cast<QMetaObjectWrapper*>(this));
+ object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
return object.asReturnedValue();
}
@@ -2143,7 +2170,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine
}
}
-bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b)
+bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QMetaObjectWrapper>());
QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 1455acc1b3..7843289d33 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -183,7 +183,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
void destroyObject(bool lastCall);
protected:
- static bool isEqualTo(Managed *that, Managed *o);
+ static bool virtualIsEqualTo(Managed *that, Managed *o);
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true);
static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value);
@@ -193,10 +193,10 @@ protected:
static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local);
QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const;
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static PropertyAttributes query(const Managed *, String *name);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ 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 PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
+ static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -237,7 +237,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const;
QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const;
- static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const;
@@ -251,11 +251,12 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject
V4_NEEDS_DESTROY
static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static bool isEqualTo(Managed *a, Managed *b);
-
const QMetaObject *metaObject() const { return d()->metaObject; }
+protected:
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static bool virtualIsEqualTo(Managed *a, Managed *b);
+
private:
void init(ExecutionEngine *engine);
ReturnedValue constructInternal(const Value *argv, int argc) const;
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
new file mode 100644
index 0000000000..20ea39d4e5
--- /dev/null
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4reflect_p.h"
+#include "qv4symbol_p.h"
+#include "qv4runtimeapi_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4propertykey_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(Reflect);
+
+void Heap::Reflect::init()
+{
+ Object::init();
+ Scope scope(internalClass->engine);
+ ScopedObject r(scope, this);
+
+ r->defineDefaultProperty(QStringLiteral("apply"), QV4::Reflect::method_apply, 3);
+ r->defineDefaultProperty(QStringLiteral("construct"), QV4::Reflect::method_construct, 2);
+ r->defineDefaultProperty(QStringLiteral("defineProperty"), QV4::Reflect::method_defineProperty, 3);
+ r->defineDefaultProperty(QStringLiteral("deleteProperty"), QV4::Reflect::method_deleteProperty, 2);
+ r->defineDefaultProperty(QStringLiteral("get"), QV4::Reflect::method_get, 2);
+ r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), QV4::Reflect::method_getOwnPropertyDescriptor, 2);
+ r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), QV4::Reflect::method_getPrototypeOf, 1);
+ r->defineDefaultProperty(QStringLiteral("has"), QV4::Reflect::method_has, 2);
+ r->defineDefaultProperty(QStringLiteral("isExtensible"), QV4::Reflect::method_isExtensible, 1);
+ r->defineDefaultProperty(QStringLiteral("ownKeys"), QV4::Reflect::method_ownKeys, 1);
+ r->defineDefaultProperty(QStringLiteral("preventExtensions"), QV4::Reflect::method_preventExtensions, 1);
+ r->defineDefaultProperty(QStringLiteral("set"), QV4::Reflect::method_set, 3);
+ r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), QV4::Reflect::method_setPrototypeOf, 2);
+}
+
+struct CallArgs {
+ Value *argv;
+ int argc;
+};
+
+static CallArgs createListFromArrayLike(Scope &scope, const Object *o)
+{
+ int len = o->getLength();
+ Value *arguments = scope.alloc(len);
+
+ for (int i = 0; i < len; ++i) {
+ arguments[i] = o->get(i);
+ if (scope.hasException())
+ return { nullptr, 0 };
+ }
+ return { arguments, len };
+}
+
+ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject())
+ return scope.engine->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv + 2);
+ CallArgs arguments = createListFromArrayLike(scope, o);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(argv[0]).call(&argv[1], arguments.argv, arguments.argc);
+}
+
+ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (argc < 2 || !argv[0].isFunctionObject() || !argv[1].isObject())
+ return scope.engine->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv + 1);
+ CallArgs arguments = createListFromArrayLike(scope, o);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(argv[0]).callAsConstructor(arguments.argv, arguments.argc);
+}
+
+ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0]);
+ ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue());
+ ScopedProperty pd(scope);
+ PropertyAttributes attrs;
+ ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ bool result = O->defineOwnProperty(name, pd, attrs);
+
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!argc || !argv[0].isObject())
+ return e->throwTypeError();
+
+ bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Primitive::undefinedValue());
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Primitive::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+ ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ ScopedValue receiver(scope, argc > 2 ? argv[2] : *o);
+
+ return Encode(o->get(name, receiver));
+}
+
+ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc);
+}
+
+ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv);
+ Heap::Object *p = o->getPrototypeOf();
+ return (p ? p->asReturnedValue() : Encode::null());
+}
+
+ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Primitive::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+
+ ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return false;
+
+ bool hasProperty = false;
+ (void) o->get(name, nullptr, &hasProperty);
+ return Encode(hasProperty);
+}
+
+ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv);
+ return Encode(o->isExtensible());
+}
+
+
+ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ return ObjectPrototype::method_getOwnPropertyNames(f, thisObject, argv, argc);
+}
+
+ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ return Encode(o->preventExtensions());
+}
+
+ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Primitive::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+ const Value &val = argc > 2 ? argv[2] : undef;
+ ScopedValue receiver(scope, argc >3 ? argv[3] : argv[0]);
+
+ ScopedPropertyKey propertyKey(scope, index->toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return false;
+ bool result = o->put(propertyKey, val, receiver);
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject()))
+ return f->engine()->throwTypeError();
+
+ Scope scope(f);
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ const Object *proto = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1);
+ return Encode(o->setPrototypeOf(proto));
+}
diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h
new file mode 100644
index 0000000000..d480e1d914
--- /dev/null
+++ b/src/qml/jsruntime/qv4reflect_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4REFLECT_H
+#define QV4REFLECT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+struct Reflect : Object {
+ void init();
+};
+
+}
+
+struct Reflect : Object {
+ V4_OBJECT2(Reflect, Object)
+
+ static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_construct(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_deleteProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_ownKeys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 000e2c3a7e..8429b96baa 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -45,10 +45,6 @@
#include "qv4scopedvalue_p.h"
#include "qv4jscall_p.h"
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
#include "private/qlocale_tools_p.h"
#include <QtCore/QDebug>
@@ -214,7 +210,7 @@ void Heap::RegExpCtor::clearLastMatch()
lastMatchEnd = 0;
}
-ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
+ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *)
{
Scope scope(fo->engine());
ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue());
@@ -263,14 +259,14 @@ ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Valu
return Encode(scope.engine->newRegExpObject(regexp));
}
-ReturnedValue RegExpCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
if (argc > 0 && argv[0].as<RegExpObject>()) {
if (argc == 1 || argv[1].isUndefined())
return Encode(argv[0]);
}
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
@@ -280,7 +276,8 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
ScopedObject ctor(scope, constructor);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(2));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(2));
+ ctor->addSymbolSpecies();
// Properties deprecated in the spec but required by "the web" :(
ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, nullptr);
@@ -387,7 +384,7 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value
}
// fill in result data
- ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype()));
+ ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray)));
int len = r->value()->captureCount();
array->arrayReserve(len);
ScopedValue v(scope);
@@ -442,12 +439,12 @@ ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Val
return Encode::undefined();
}
-template <int index>
+template <uint index>
ReturnedValue RegExpPrototype::method_get_lastMatch_n(const FunctionObject *b, const Value *, const Value *, int)
{
Scope scope(b);
ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
- ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined());
+ ScopedValue res(scope, lastMatch ? lastMatch->get(index) : Encode::undefined());
if (res->isUndefined())
res = scope.engine->newString();
return res->asReturnedValue();
@@ -457,7 +454,7 @@ ReturnedValue RegExpPrototype::method_get_lastParen(const FunctionObject *b, con
{
Scope scope(b);
ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
- ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined());
+ ScopedValue res(scope, lastMatch ? lastMatch->get(lastMatch->getLength() - 1) : Encode::undefined());
if (res->isUndefined())
res = scope.engine->newString();
return res->asReturnedValue();
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 181628241b..0d4fe760eb 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -129,11 +129,11 @@ struct RegExpObject: Object {
void initProperties();
int lastIndex() const {
- Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()));
+ Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->propertyKey()));
return propertyData(Index_LastIndex)->toInt32();
}
void setLastIndex(int index) {
- Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()));
+ Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->propertyKey()));
return setProperty(Index_LastIndex, Primitive::fromInt32(index));
}
@@ -152,8 +152,8 @@ struct RegExpCtor: FunctionObject
int lastMatchStart() { return d()->lastMatchStart; }
int lastMatchEnd() { return d()->lastMatchEnd; }
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct RegExpPrototype: RegExpObject
@@ -165,7 +165,7 @@ struct RegExpPrototype: RegExpObject
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- template <int index>
+ template <uint index>
static ReturnedValue method_get_lastMatch_n(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_input(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9729228511..f387285e37 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -61,6 +61,8 @@
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include "qv4qobjectwrapper_p.h"
+#include "qv4symbol_p.h"
+#include "qv4generatorobject_p.h"
#include <private/qv8engine_p.h>
#endif
@@ -314,37 +316,23 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId)
QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId];
Q_ASSERT(clos);
ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ if (clos->isGenerator())
+ return GeneratorFunction::create(current, clos)->asReturnedValue();
return FunctionObject::createScriptFunction(current, clos)->asReturnedValue();
}
-bool Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index)
+bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index)
{
Scope scope(engine);
- ScopedObject o(scope, base);
- if (o) {
- uint n = index.asArrayIndex();
- if (n < UINT_MAX)
- return o->deleteIndexedProperty(n);
- }
-
- ScopedString name(scope, index.toString(engine));
- return method_deleteMemberString(engine, base, name);
-}
-
-bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex)
-{
- Scope scope(engine);
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- return method_deleteMemberString(engine, base, name);
-}
-
-bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name)
-{
- Scope scope(engine);
- ScopedObject obj(scope, base.toObject(engine));
+ ScopedObject o(scope, base.toObject(engine));
if (scope.engine->hasException)
return Encode::undefined();
- return obj->deleteProperty(name);
+ Q_ASSERT(o);
+
+ ScopedPropertyKey key(scope, index.toPropertyKey(engine));
+ if (engine->hasException)
+ return false;
+ return o->deleteProperty(key);
}
bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex)
@@ -361,8 +349,16 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val
if (!rhs)
return engine->throwTypeError();
- // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result.
- return rhs->instanceOf(lval);
+ Scope scope(engine);
+ ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance()));
+ if (hasInstance->isUndefined())
+ return rhs->instanceOf(lval);
+ FunctionObject *f = hasInstance->as<FunctionObject>();
+ if (!f)
+ return engine->throwTypeError();
+
+ ScopedValue result(scope, f->call(&rval, &lval, 1));
+ return Encode(result->toBoolean());
}
QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right)
@@ -371,7 +367,7 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left
if (!ro)
return engine->throwTypeError();
Scope scope(engine);
- ScopedString s(scope, left.toString(engine));
+ ScopedPropertyKey s(scope, left.toPropertyKey(engine));
if (scope.hasException())
return Encode::undefined();
bool r = ro->hasProperty(s);
@@ -408,27 +404,59 @@ Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double n
ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint)
{
- if (typeHint == PREFERREDTYPE_HINT) {
- if (object->as<DateObject>())
- typeHint = STRING_HINT;
- else
- typeHint = NUMBER_HINT;
+ ExecutionEngine *engine = object->internalClass()->engine;
+ if (engine->hasException)
+ return Encode::undefined();
+
+ String *hint;
+ switch (typeHint) {
+ case STRING_HINT:
+ hint = engine->id_string();
+ break;
+ case NUMBER_HINT:
+ hint = engine->id_number();
+ break;
+ default:
+ hint = engine->id_default();
+ break;
}
- ExecutionEngine *engine = object->internalClass()->engine;
+ Scope scope(engine);
+ ScopedFunctionObject toPrimitive(scope, object->get(engine->symbol_toPrimitive()));
if (engine->hasException)
return Encode::undefined();
+ if (toPrimitive) {
+ ScopedValue result(scope, toPrimitive->call(object, hint, 1));
+ if (engine->hasException)
+ return Encode::undefined();
+ if (!result->isPrimitive())
+ return engine->throwTypeError();
+ return result->asReturnedValue();
+ }
+
+ if (hint == engine->id_default())
+ hint = engine->id_number();
+ return ordinaryToPrimitive(engine, object, hint);
+}
+
+
+ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint)
+{
+ Q_ASSERT(!engine->hasException);
String *meth1 = engine->id_toString();
String *meth2 = engine->id_valueOf();
- if (typeHint == NUMBER_HINT)
+ if (typeHint->propertyKey() == engine->id_number()->propertyKey()) {
qSwap(meth1, meth2);
+ } else {
+ Q_ASSERT(typeHint->propertyKey() == engine->id_string()->propertyKey());
+ }
Scope scope(engine);
ScopedValue result(scope);
- ScopedValue conv(scope, object->get(meth1));
+ ScopedValue conv(scope, object->get(meth1));
if (FunctionObject *o = conv->as<FunctionObject>()) {
result = o->call(object, nullptr, 0);
if (result->isPrimitive())
@@ -449,19 +477,22 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH
}
-
Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Value &value)
{
Q_ASSERT(!value.isObject());
switch (value.type()) {
case Value::Undefined_Type:
+ engine->throwTypeError(QLatin1String("Value is undefined and could not be converted to an object"));
+ return nullptr;
case Value::Null_Type:
- engine->throwTypeError();
+ engine->throwTypeError(QLatin1String("Value is null and could not be converted to an object"));
return nullptr;
case Value::Boolean_Type:
return engine->newBooleanObject(value.booleanValue());
case Value::Managed_Type:
- Q_ASSERT(value.isString());
+ Q_ASSERT(value.isStringOrSymbol());
+ if (!value.isString())
+ return engine->newSymbolObject(value.symbolValue());
return engine->newStringObject(value.stringValue());
case Value::Integer_Type:
default: // double
@@ -488,6 +519,10 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val
case Value::Managed_Type: {
if (value.isString())
return static_cast<const String &>(value).d();
+ if (value.isSymbol()) {
+ engine->throwTypeError(QLatin1String("Cannot convert a symbol to a string."));
+ return nullptr;
+ }
value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint));
Q_ASSERT(value.isPrimitive());
if (value.isString())
@@ -539,14 +574,14 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu
return Encode(x + y);
}
-bool Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
+void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
{
Scope scope(engine);
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ QV4::Function *v4Function = engine->currentStackFrame->v4Function;
+ ScopedString name(scope, v4Function->compilationUnit->runtimeStrings[nameIndex]);
ScopedObject o(scope, object.toObject(engine));
- if (!o)
- return false;
- return o->put(name, value);
+ if ((!o || !o->put(name, value)) && v4Function->isStrict())
+ engine->throwTypeError();
}
static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx)
@@ -579,12 +614,13 @@ static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engin
return v->asReturnedValue();
}
- return o->getIndexed(idx);
+ return o->get(idx);
}
static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index)
{
- Q_ASSERT(index.asArrayIndex() == UINT_MAX);
+ Q_ASSERT(!index.isPositiveInt());
+
Scope scope(engine);
ScopedObject o(scope, object);
@@ -598,26 +634,18 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine,
Q_ASSERT(!!o); // can't fail as null/undefined is covered above
}
- ScopedString name(scope, index.toString(engine));
+ ScopedPropertyKey name(scope, index.toPropertyKey(engine));
if (scope.hasException())
return Encode::undefined();
return o->get(name);
}
-/* load element:
-
- Managed *m = object.heapObject();
- if (m)
- return m->internalClass->getIndexed(m, index);
- return getIndexedFallback(object, index);
-*/
-
ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index)
{
- uint idx = 0;
- if (index.asArrayIndex(idx)) {
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
+ if (b->internalClass->vtable->isObject) {
Heap::Object *o = static_cast<Heap::Object *>(b);
if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
@@ -640,8 +668,8 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val
if (engine->hasException)
return false;
- uint idx = 0;
- if (index.asArrayIndex(idx)) {
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
if (idx < s->values.size) {
@@ -649,53 +677,136 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val
return true;
}
}
- return o->putIndexed(idx, value);
+ return o->put(idx, value);
}
- ScopedString name(scope, index.toString(engine));
+ ScopedPropertyKey name(scope, index.toPropertyKey(engine));
+ if (engine->hasException)
+ return false;
return o->put(name, value);
}
-bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
- uint idx = 0;
- if (index.asArrayIndex(idx)) {
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
+ if (b->internalClass->vtable->isObject) {
Heap::Object *o = static_cast<Heap::Object *>(b);
if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
if (idx < s->values.size) {
s->setData(engine, idx, value);
- return true;
+ return;
}
}
}
}
}
- return setElementFallback(engine, object, index, value);
+ if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict())
+ engine->throwTypeError();
}
-ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in)
+ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator)
{
Scope scope(engine);
ScopedObject o(scope, (Object *)nullptr);
if (!in.isNullOrUndefined())
o = in.toObject(engine);
- return engine->newForEachIteratorObject(o)->asReturnedValue();
+ if (engine->hasException)
+ return Encode::undefined();
+ if (iterator) {
+ if (!o)
+ return engine->throwTypeError();
+ ScopedFunctionObject f(scope, o->get(engine->symbol_iterator()));
+ if (!f)
+ return engine->throwTypeError();
+ JSCallData cData(scope, 0, nullptr, o);
+ ScopedObject it(scope, f->call(cData));
+ if (!it)
+ return engine->throwTypeError();
+ return it->asReturnedValue();
+ }
+ return engine->newForInIteratorObject(o)->asReturnedValue();
}
-ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator)
+ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value)
{
- Q_ASSERT(foreach_iterator.isObject());
+ Q_ASSERT(iterator.isObject());
- ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue());
- Q_ASSERT(it->as<ForEachIteratorObject>());
+ Scope scope(engine);
+ ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
+ if (!f)
+ return engine->throwTypeError();
+ JSCallData cData(scope, 0, nullptr, &iterator);
+ ScopedObject o(scope, f->call(cData));
+ if (!o)
+ return engine->throwTypeError();
+ ScopedValue d(scope, o->get(engine->id_done()));
+ bool done = d->toBoolean();
+ if (done) {
+ *value = Encode::undefined();
+ } else {
+ *value = o->get(engine->id_value());
+ }
+ return Encode(done);
+}
- return it->nextPropertyName();
+ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done)
+{
+ Q_ASSERT(iterator.isObject());
+ Q_ASSERT(done.isBoolean());
+ if (done.booleanValue())
+ return Encode::undefined();
+
+ Scope scope(engine);
+ bool hadException = engine->hasException;
+ ScopedValue e(scope);
+ if (hadException) {
+ e = *engine->exceptionValue;
+ engine->hasException = false;
+ }
+ ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
+ ScopedObject o(scope);
+ if (f) {
+ JSCallData cData(scope, 0, nullptr, &iterator);
+ o = f->call(cData);
+ }
+ if (hadException || !f) {
+ *engine->exceptionValue = e;
+ engine->hasException = hadException;
+ return Encode::undefined();
+ }
+ if (engine->hasException)
+ return Encode::undefined();
+
+ if (!o)
+ return engine->throwTypeError();
+ return Encode::undefined();
}
+ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator)
+{
+ Q_ASSERT(iterator.isObject());
+
+ Scope scope(engine);
+ ScopedArrayObject array(scope, engine->newArrayObject());
+ array->arrayCreate();
+ uint index = 0;
+ while (1) {
+ ScopedValue n(scope);
+ ScopedValue done(scope, method_iteratorNext(engine, iterator, n));
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(done->isBoolean());
+ if (done->booleanValue())
+ break;
+ array->arraySet(index, n);
+ ++index;
+ }
+ return array->asReturnedValue();
+}
void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value)
{
@@ -745,6 +856,42 @@ ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex)
return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name);
}
+ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property)
+{
+ Scope scope(engine);
+ ScopedObject base(scope, engine->currentStackFrame->thisObject());
+ if (!base)
+ return engine->throwTypeError();
+ ScopedObject proto(scope, base->getPrototypeOf());
+ if (!proto)
+ return engine->throwTypeError();
+ ScopedPropertyKey key(scope, property.toPropertyKey(engine));
+ if (engine->hasException)
+ return Encode::undefined();
+ return proto->get(key, base);
+}
+
+void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value)
+{
+ Scope scope(engine);
+ ScopedObject base(scope, engine->currentStackFrame->thisObject());
+ if (!base) {
+ engine->throwTypeError();
+ return;
+ }
+ ScopedObject proto(scope, base->getPrototypeOf());
+ if (!proto) {
+ engine->throwTypeError();
+ return;
+ }
+ ScopedPropertyKey key(scope, property.toPropertyKey(engine));
+ if (engine->hasException)
+ return;
+ bool result = proto->put(key, value, base);
+ if (!result && engine->currentStackFrame->v4Function->isStrict())
+ engine->throwTypeError();
+}
+
#endif // V4_BOOTSTRAP
uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
@@ -1033,24 +1180,31 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V
ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)
{
Scope scope(engine);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedObject lookupObject(scope, base);
- if (!base->isObject()) {
+ if (!lookupObject) {
Q_ASSERT(!base->isEmpty());
if (base->isNullOrUndefined()) {
QString message = QStringLiteral("Cannot call method '%1' of %2")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
- base->toQStringNoThrow());
+ .arg(name->toQString(), base->toQStringNoThrow());
return engine->throwTypeError(message);
}
- ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base));
- if (engine->hasException) // type error
- return Encode::undefined();
- base = thisObject;
+ if (base->isManaged()) {
+ Managed *m = static_cast<Managed *>(base);
+ lookupObject = m->internalClass()->prototype;
+ Q_ASSERT(m->internalClass()->prototype);
+ } else {
+ lookupObject = RuntimeHelpers::convertToObject(engine, *base);
+ if (engine->hasException) // type error
+ return Encode::undefined();
+ if (!engine->currentStackFrame->v4Function->isStrict())
+ base = lookupObject;
+ }
}
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name));
+ ScopedFunctionObject f(scope, static_cast<Object *>(lookupObject)->get(name));
if (!f) {
QString error = QStringLiteral("Property '%1' of object %2 is not a function")
@@ -1080,7 +1234,7 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base,
ScopedValue thisObject(scope, base->toObject(engine));
base = thisObject;
- ScopedString str(scope, index.toString(engine));
+ ScopedPropertyKey str(scope, index.toPropertyKey(engine));
if (engine->hasException)
return Encode::undefined();
@@ -1130,12 +1284,80 @@ ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engi
return fo->call(qmlContextValue, argv, argc);
}
-ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc)
+struct CallArgs {
+ Value *argv;
+ int argc;
+};
+
+static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
+{
+ ScopedValue it(scope);
+ ScopedValue done(scope);
+
+ int argCount = 0;
+
+ Value *v = scope.alloc<Scope::Uninitialized>();
+ Value *arguments = v;
+ for (int i = 0; i < argc; ++i) {
+ if (!argv[i].isEmpty()) {
+ *v = argv[i];
+ ++argCount;
+ v = scope.alloc<Scope::Uninitialized>();
+ continue;
+ }
+ // spread element
+ ++i;
+ it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1);
+ if (scope.engine->hasException)
+ return { nullptr, 0 };
+ while (1) {
+ done = Runtime::method_iteratorNext(scope.engine, it, v);
+ if (scope.engine->hasException)
+ return { nullptr, 0 };
+ Q_ASSERT(done->isBoolean());
+ if (done->booleanValue())
+ break;
+ ++argCount;
+ v = scope.alloc<Scope::Uninitialized>();
+ }
+ }
+ return { arguments, argCount };
+}
+
+ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc)
+{
+ Q_ASSERT(argc >= 1);
+ if (!function.isFunctionObject())
+ return engine->throwTypeError();
+
+ Scope scope(engine);
+ CallArgs arguments = createSpreadArguments(scope, argv, argc);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc);
+}
+
+ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
{
if (!function.isFunctionObject())
return engine->throwTypeError();
- return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc);
+ return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget);
+}
+
+ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
+{
+ Q_UNIMPLEMENTED();
+ if (!function.isFunctionObject())
+ return engine->throwTypeError();
+
+ Scope scope(engine);
+ CallArgs arguments = createSpreadArguments(scope, argv, argc);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget);
}
void Runtime::method_throwException(ExecutionEngine *engine, const Value &value)
@@ -1161,6 +1383,8 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &
case Value::Managed_Type:
if (value.isString())
res = engine->id_string();
+ else if (value.isSymbol())
+ res = engine->id_symbol();
else if (value.objectValue()->as<FunctionObject>())
res = engine->id_function();
else
@@ -1183,25 +1407,54 @@ QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameI
return method_typeofValue(engine, prop);
}
-/* The next three methods are a bit tricky. They can't open up a Scope, as that
- * would mess up the pushing of the context.
- *
- * Instead the push/pop pair acts as a non local scope.
- */
-ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o)
+ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame)
{
- Q_ASSERT(o.isObject());
- const Object &obj = static_cast<const Object &>(o);
- return parent->newWithContext(obj.d())->asReturnedValue();
+ QV4::Value &accumulator = jsStackFrame[CallData::Accumulator];
+ accumulator = accumulator.toObject(engine);
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(accumulator.isObject());
+ const Object &obj = static_cast<const Object &>(accumulator);
+ ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context);
+ return context->newWithContext(obj.d())->asReturnedValue();
}
-ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex)
+ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)
{
ExecutionEngine *e = parent->engine();
- return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex],
- e->catchException(nullptr))->asReturnedValue();
+ return parent->newCatchContext(e->currentStackFrame, blockIndex,
+ e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue();
}
+ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index)
+{
+ ExecutionEngine *e = parent->engine();
+ return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue();
+}
+
+ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous)
+{
+ return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue();
+}
+
+
+ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index)
+{
+ Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext ||
+ engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext);
+ ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
+ engine->setScriptContext(c);
+ return c;
+}
+
+ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine)
+{
+ ReturnedValue root = engine->rootContext()->asReturnedValue();
+ engine->setScriptContext(root);
+ return root;
+}
+
+
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
{
Scope scope(engine);
@@ -1214,61 +1467,160 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value
return engine->newArrayObject(values, length)->asReturnedValue();
}
-ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)
+ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, int argc, const QV4::Value *args)
{
Scope scope(engine);
- QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId];
- ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype()));
+ Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]);
+ ScopedObject o(scope, engine->newObject(klass->d()));
- {
- bool needSparseArray = arrayGetterSetterCountAndFlags >> 30;
- if (needSparseArray)
- o->initSparseArray();
- }
+ Q_ASSERT(uint(argc) >= klass->d()->size);
- for (uint i = 0; i < klass->size; ++i)
+ for (uint i = 0; i < klass->d()->size; ++i)
o->setProperty(i, *args++);
- if (arrayValueCount > 0) {
- ScopedValue entry(scope);
- for (int i = 0; i < arrayValueCount; ++i) {
- uint idx = args->toUInt32();
- ++args;
- entry = *args++;
- o->arraySet(idx, entry);
+ Q_ASSERT((argc - klass->d()->size) % 3 == 0);
+ int additionalArgs = (argc - int(klass->d()->size))/3;
+
+ if (!additionalArgs)
+ return o->asReturnedValue();
+
+ ScopedPropertyKey name(scope);
+ ScopedProperty pd(scope);
+ for (int i = 0; i < additionalArgs; ++i) {
+ Q_ASSERT(args->isInteger());
+ ObjectLiteralArgument arg = ObjectLiteralArgument(args->integerValue());
+ name = args[1].toPropertyKey(engine);
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(arg == ObjectLiteralArgument::Value || args[2].isFunctionObject());
+ if (arg == ObjectLiteralArgument::Value || arg == ObjectLiteralArgument::Getter) {
+ pd->value = args[2];
+ pd->set = Primitive::emptyValue();
+ } else {
+ pd->value = Primitive::emptyValue();
+ pd->set = args[2];
}
+ bool ok = o->defineOwnProperty(name, pd, (arg == ObjectLiteralArgument::Value ? Attr_Data : Attr_Accessor));
+ if (!ok)
+ return engine->throwTypeError();
+
+ args += 3;
}
+ return o.asReturnedValue();
+}
+
+ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames)
+{
+ const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit;
+ const QV4::CompiledData::Class *cls = unit->data->classAt(classIndex);
- uint arrayGetterSetterCount = arrayGetterSetterCountAndFlags & ((1 << 30) - 1);
- if (arrayGetterSetterCount > 0) {
- ScopedProperty pd(scope);
- for (uint i = 0; i < arrayGetterSetterCount; ++i) {
- uint idx = args->toUInt32();
- ++args;
- pd->value = *args;
- ++args;
- pd->set = *args;
- ++args;
- o->arraySet(idx, pd, Attr_Accessor);
+ Scope scope(engine);
+ ScopedObject protoParent(scope, engine->objectPrototype());
+ ScopedObject constructorParent(scope, engine->functionPrototype());
+ if (!superClass.isEmpty()) {
+ if (superClass.isNull()) {
+ protoParent = Encode::null();
+ } else {
+ // ### check that the heritage object is a constructor
+ if (!superClass.isFunctionObject())
+ return engine->throwTypeError(QStringLiteral("The superclass is not a function object."));
+ const FunctionObject *s = static_cast<const FunctionObject *>(&superClass);
+ ScopedValue result(scope, s->get(scope.engine->id_prototype()));
+ if (!result->isObject() && !result->isNull())
+ return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object."));
+ protoParent = *result;
+ constructorParent = superClass;
}
}
- return o.asReturnedValue();
+ ScopedObject proto(scope, engine->newObject());
+ proto->setPrototypeUnchecked(protoParent);
+ ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+
+ ScopedFunctionObject constructor(scope);
+ QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr;
+ constructor = FunctionObject::createConstructorFunction(current, f, !superClass.isEmpty())->asReturnedValue();
+ constructor->setPrototypeUnchecked(constructorParent);
+ constructor->defineDefaultProperty(engine->id_prototype(), proto);
+ proto->defineDefaultProperty(engine->id_constructor(), constructor);
+
+ ScopedString name(scope);
+ if (cls->nameIndex != UINT_MAX) {
+ name = unit->runtimeStrings[cls->nameIndex];
+ constructor->defineReadonlyConfigurableProperty(engine->id_name(), name);
+ }
+
+ ScopedObject receiver(scope, *constructor);
+ ScopedPropertyKey propertyName(scope);
+ ScopedFunctionObject function(scope);
+ ScopedProperty property(scope);
+ const CompiledData::Method *methods = cls->methodTable();
+ for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
+ if (i == cls->nStaticMethods)
+ receiver = proto;
+ if (methods[i].name == UINT_MAX) {
+ propertyName = computedNames->toPropertyKey(engine);
+ if (engine->hasException)
+ return Encode::undefined();
+ ++computedNames;
+ } else {
+ name = unit->runtimeStrings[methods[i].name];
+ propertyName = name->toPropertyKey();
+ }
+ QV4::Function *f = unit->runtimeFunctions[methods[i].function];
+ Q_ASSERT(f);
+ if (f->isGenerator())
+ function = MemberGeneratorFunction::create(current, f);
+ else
+ function = FunctionObject::createMemberFunction(current, f);
+ Q_ASSERT(function);
+ PropertyAttributes attributes;
+ switch (methods[i].type) {
+ case CompiledData::Method::Getter:
+ property->setGetter(function);
+ property->set = Primitive::emptyValue();
+ attributes = Attr_Accessor|Attr_NotEnumerable;
+ break;
+ case CompiledData::Method::Setter:
+ property->value = Primitive::emptyValue();
+ property->setSetter(function);
+ attributes = Attr_Accessor|Attr_NotEnumerable;
+ break;
+ default: // Regular
+ property->value = function;
+ property->set = Primitive::emptyValue();
+ attributes = Attr_Data|Attr_NotEnumerable;
+ break;
+ }
+ receiver->defineOwnProperty(propertyName, property, attributes);
+ }
+
+ return constructor->asReturnedValue();
}
QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine)
{
Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext);
- QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject];
- return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject);
+ return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}
QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine)
{
- QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject];
- return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject);
+ return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
+}
+
+QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex)
+{
+ const Value *values = engine->currentStackFrame->originalArguments + argIndex;
+ int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex;
+ if (nValues <= 0)
+ return engine->newArrayObject(0)->asReturnedValue();
+ return engine->newArrayObject(values, nValues)->asReturnedValue();
}
+
ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine)
{
Heap::QmlContext *ctx = engine->qmlContext();
@@ -1485,24 +1837,133 @@ ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right)
return Encode(r);
}
+struct LazyScope
+{
+ ExecutionEngine *engine = nullptr;
+ Value *stackMark = nullptr;
+ ~LazyScope() {
+ if (engine)
+ engine->jsStackTop = stackMark;
+ }
+ template <typename T>
+ void set(Value **scopedValue, T value, ExecutionEngine *e) {
+ if (!engine) {
+ engine = e;
+ stackMark = engine->jsStackTop;
+ }
+ if (!*scopedValue)
+ *scopedValue = e->jsAlloca(1);
+ **scopedValue = value;
+ }
+};
+
Bool Runtime::method_compareEqual(const Value &left, const Value &right)
{
TRACE2(left, right);
- if (left.rawValue() == right.rawValue())
- // NaN != NaN
- return !left.isNaN();
+ Value lhs = left;
+ Value rhs = right;
- if (left.type() == right.type()) {
- if (left.isDouble() && left.doubleValue() == 0 && right.doubleValue() == 0)
- return true; // this takes care of -0 == +0 (which obviously have different raw values)
- if (!left.isManaged())
- return false;
- if (left.isString() == right.isString())
- return left.cast<Managed>()->isEqualTo(right.cast<Managed>());
+#ifndef V4_BOOTSTRAP
+ LazyScope scope;
+ Value *lhsGuard = nullptr;
+ Value *rhsGuard = nullptr;
+#endif
+
+ redo:
+ if (lhs.asReturnedValue() == rhs.asReturnedValue())
+ return !lhs.isNaN();
+
+ int lt = lhs.quickType();
+ int rt = rhs.quickType();
+ if (rt < lt) {
+ qSwap(lhs, rhs);
+ qSwap(lt, rt);
}
- return RuntimeHelpers::equalHelper(left, right);
+ switch (lt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ if (lhs.isUndefined())
+ return rhs.isNullOrUndefined();
+ Q_FALLTHROUGH();
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3:
+ // LHS: Managed
+ switch (rt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ if (rhs.isUndefined())
+ return false;
+ Q_FALLTHROUGH();
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3: {
+#ifndef V4_BOOTSTRAP
+ // RHS: Managed
+ Heap::Base *l = lhs.m();
+ Heap::Base *r = rhs.m();
+ Q_ASSERT(l);
+ Q_ASSERT(r);
+ if (l->internalClass->vtable->isStringOrSymbol == r->internalClass->vtable->isStringOrSymbol)
+ return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
+ if (l->internalClass->vtable->isStringOrSymbol) {
+ scope.set(&rhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT), r->internalClass->engine);
+ rhs = rhsGuard->asReturnedValue();
+ break;
+ } else {
+ Q_ASSERT(r->internalClass->vtable->isStringOrSymbol);
+ scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), l->internalClass->engine);
+ lhs = lhsGuard->asReturnedValue();
+ break;
+ }
+#endif
+ return false;
+ }
+ case QV4::Value::QT_Empty:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Null:
+ return false;
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ rhs = Primitive::fromDouble(rhs.int_32());
+ // fall through
+ default: // double
+#ifndef V4_BOOTSTRAP
+ if (lhs.m()->internalClass->vtable->isStringOrSymbol) {
+ return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
+ } else {
+ scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine);
+ lhs = lhsGuard->asReturnedValue();
+ }
+#else
+ Q_UNIMPLEMENTED();
+#endif
+ }
+ goto redo;
+ case QV4::Value::QT_Empty:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Null:
+ return rhs.isNull();
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ switch (rt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3:
+ case QV4::Value::QT_Empty:
+ case QV4::Value::QT_Null:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ return lhs.int_32() == rhs.int_32();
+ default: // double
+ return lhs.int_32() == rhs.doubleValue();
+ }
+ default: // double
+ Q_ASSERT(rhs.isDouble());
+ return lhs.doubleValue() == rhs.doubleValue();
+ }
}
ReturnedValue Runtime::method_equal(const Value &left, const Value &right)
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index 3a26c23990..72af90d1dc 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -100,6 +100,7 @@ enum TypeHint {
struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
static ReturnedValue objectDefaultValue(const Object *object, int typeHint);
static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint);
+ static ReturnedValue ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint);
static double stringToNumber(const QString &s);
static Heap::String *stringFromNumber(ExecutionEngine *engine, double number);
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 91232256a9..3f65e6c6d6 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -99,33 +99,39 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \
F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \
+ F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \
\
/* construct */ \
- F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
+ F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \
+ F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \
\
/* load & store */ \
F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
- F(bool, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
- F(bool, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
+ F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
+ F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \
F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \
F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \
+ F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \
+ F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \
\
/* typeof */ \
F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \
F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \
\
/* delete */ \
- F(bool, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \
- F(bool, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \
- F(bool, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \
+ F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \
F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \
\
/* exceptions & scopes */ \
F(void, throwException, (ExecutionEngine *engine, const Value &value)) \
- F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \
- F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \
+ F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \
+ F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \
+ F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \
+ F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \
+ F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \
+ F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \
\
/* closures */ \
F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
@@ -134,14 +140,18 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \
F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \
F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \
+ F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \
\
/* literals */ \
F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \
- F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \
- \
- /* foreach */ \
- F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \
- F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \
+ F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, int argc, const Value *args)) \
+ F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \
+ \
+ /* for-in, for-of and array destructuring */ \
+ F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \
+ F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \
+ F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \
+ F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \
\
/* unary operators */ \
F(ReturnedValue, uMinus, (const Value &value)) \
diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp
index fe18ddf9ed..9866966936 100644
--- a/src/qml/jsruntime/qv4runtimecodegen.cpp
+++ b/src/qml/jsruntime/qv4runtimecodegen.cpp
@@ -52,13 +52,16 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName,
_module->finalUrl = fileName;
_context = nullptr;
- Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode);
+ Compiler::ScanFunctions scan(this, sourceCode, Compiler::ContextType::Global);
// fake a global environment
- scan.enterEnvironment(nullptr, Compiler::FunctionCode);
+ scan.enterEnvironment(nullptr, Compiler::ContextType::Function, QString());
scan(ast);
scan.leaveEnvironment();
- int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr);
+ if (hasError)
+ return;
+
+ int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
_module->rootContext = _module->functions.at(index);
}
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index bb20f384b3..1f66c4a47e 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -53,6 +53,7 @@
#include "qv4engine_p.h"
#include "qv4value_p.h"
#include "qv4property_p.h"
+#include "qv4propertykey_p.h"
#ifdef V4_USE_VALGRIND
#include <valgrind/memcheck.h>
@@ -117,8 +118,45 @@ struct Scope {
engine->jsStackTop = mark;
}
- QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const {
- return engine->jsAlloca(nValues);
+ enum AllocMode {
+ Undefined,
+ Empty,
+ /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */
+ Uninitialized
+ };
+ template <AllocMode mode = Undefined>
+ QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const
+ {
+ Value *ptr = engine->jsAlloca(nValues);
+ switch (mode) {
+ case Undefined:
+ for (int i = 0; i < nValues; ++i)
+ ptr[i] = Primitive::undefinedValue();
+ break;
+ case Empty:
+ for (int i = 0; i < nValues; ++i)
+ ptr[i] = Primitive::emptyValue();
+ break;
+ case Uninitialized:
+ break;
+ }
+ return ptr;
+ }
+ template <AllocMode mode = Undefined>
+ QML_NEARLY_ALWAYS_INLINE Value *alloc() const
+ {
+ Value *ptr = engine->jsAlloca(1);
+ switch (mode) {
+ case Undefined:
+ *ptr = Primitive::undefinedValue();
+ break;
+ case Empty:
+ *ptr = Primitive::emptyValue();
+ break;
+ case Uninitialized:
+ break;
+ }
+ return ptr;
}
bool hasException() const {
@@ -136,31 +174,31 @@ struct ScopedValue
{
ScopedValue(const Scope &scope)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(0);
}
ScopedValue(const Scope &scope, const Value &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
*ptr = v;
}
ScopedValue(const Scope &scope, Heap::Base *o)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setM(o);
}
ScopedValue(const Scope &scope, Managed *m)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(m->asReturnedValue());
}
ScopedValue(const Scope &scope, const ReturnedValue &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(v);
}
@@ -203,6 +241,37 @@ struct ScopedValue
Value *ptr;
};
+
+struct ScopedPropertyKey
+{
+ ScopedPropertyKey(const Scope &scope)
+ {
+ ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>());
+ *ptr = PropertyKey::invalid();
+ }
+
+ ScopedPropertyKey(const Scope &scope, const PropertyKey &v)
+ {
+ ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>());
+ *ptr = v;
+ }
+
+ ScopedPropertyKey &operator=(const PropertyKey &other) {
+ *ptr = other;
+ return *this;
+ }
+
+ PropertyKey *operator->() {
+ return ptr;
+ }
+ operator PropertyKey() const {
+ return *ptr;
+ }
+
+ PropertyKey *ptr;
+};
+
+
template<typename T>
struct Scoped
{
@@ -214,66 +283,66 @@ struct Scoped
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Undefined>();
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, Heap::Base *o)
{
Value v;
v = o;
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ScopedValue &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.ptr->as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v, ConvertType)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(value_convert<T>(scope.engine, v));
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value *v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v ? v->as<T>() : nullptr);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, T *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(t);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const T *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(t);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, typename T::Data *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
*ptr = t;
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(QV4::Value::fromReturnedValue(v).as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v, ConvertType)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v)));
}
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 5cd62c90f1..37c4f27ca9 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -57,10 +57,11 @@
#include <QtCore/QDebug>
#include <QtCore/QString>
+#include <QScopedValueRollback>
using namespace QV4;
-Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit)
+Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit)
: line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
, compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true)
{
@@ -128,7 +129,7 @@ void Script::parse()
RuntimeCodegen cg(v4, &jsGenerator, strictMode);
if (inheritContext)
cg.setUseFastLookups(false);
- cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, compilationMode);
+ cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, contextType);
if (v4->hasException)
return;
@@ -154,7 +155,7 @@ ReturnedValue Script::run(const QV4::Value *thisObject)
QV4::Scope valueScope(engine);
if (qmlContext.isUndefined()) {
- TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction);
+ QScopedValueRollback<Function*> savedGlobalCode(engine->globalCode, vmFunction);
return vmFunction->call(thisObject ? thisObject : engine->globalObject, nullptr, 0,
context);
@@ -171,19 +172,16 @@ Function *Script::function()
return vmFunction;
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
const QString &fileName, const QString &finalUrl, const QString &source,
- QList<QQmlError> *reportedErrors, Directives *directivesCollector)
+ QList<QQmlError> *reportedErrors)
{
using namespace QV4::Compiler;
using namespace QQmlJS::AST;
- Engine ee;
- if (directivesCollector)
- ee.setDirectives(directivesCollector);
- Lexer lexer(&ee);
+ Lexer lexer(jsEngine);
lexer.setCode(source, /*line*/1, /*qml mode*/false);
- Parser parser(&ee);
+ Parser parser(jsEngine);
parser.parseProgram();
@@ -219,7 +217,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi
Codegen cg(unitGenerator, /*strict mode*/false);
cg.setUseFastLookups(false);
- cg.generateFromProgram(fileName, finalUrl, source, program, module, GlobalCode);
+ cg.generateFromProgram(fileName, finalUrl, source, program, module, ContextType::Global);
errors = cg.qmlErrors();
if (!errors.isEmpty()) {
if (reportedErrors)
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index b4ac150044..e7189664e2 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -62,12 +62,16 @@ QT_BEGIN_NAMESPACE
class QQmlContextData;
+namespace QQmlJS {
+class Engine;
+}
+
namespace QV4 {
struct Q_QML_EXPORT Script {
- Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
+ Script(ExecutionContext *scope, QV4::Compiler::ContextType mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
- , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode)
+ , context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode)
, vmFunction(nullptr), parseAsBinding(false) {}
Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
@@ -76,7 +80,7 @@ struct Q_QML_EXPORT Script {
if (qml)
qmlContext.set(engine, *qml);
}
- Script(ExecutionEngine *engine, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit);
+ Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit);
~Script();
QString sourceFile;
int line;
@@ -86,7 +90,7 @@ struct Q_QML_EXPORT Script {
bool strictMode;
bool inheritContext;
bool parsed;
- QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode;
+ QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval;
QV4::PersistentValue qmlContext;
QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit;
Function *vmFunction;
@@ -97,10 +101,9 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QQmlRefPointer<CompiledData::CompilationUnit> precompile(
- QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+ static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
const QString &fileName, const QString &finalUrl, const QString &source,
- QList<QQmlError> *reportedErrors = nullptr, QQmlJS::Directives *directivesCollector = nullptr);
+ QList<QQmlError> *reportedErrors = nullptr);
static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error);
static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 7d29d0b517..1ba889ee82 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -227,7 +227,7 @@ namespace Heap {
template <typename Container>
struct QQmlSequence : Object {
void init(const Container &container);
- void init(QObject *object, int propertyIndex);
+ void init(QObject *object, int propertyIndex, bool readOnly);
void destroy() {
delete container;
object.destroy();
@@ -237,7 +237,8 @@ struct QQmlSequence : Object {
mutable Container *container;
QQmlQPointer<QObject> object;
int propertyIndex;
- bool isReference;
+ bool isReference : 1;
+ bool isReadOnly : 1;
};
}
@@ -294,6 +295,9 @@ public:
return false;
}
+ if (d()->isReadOnly)
+ return false;
+
if (d()->isReference) {
if (!d()->object)
return false;
@@ -345,7 +349,7 @@ public:
if (d()->isReference) {
if (!d()->object) {
- QV4::Object::advanceIterator(this, it, name, index, p, attrs);
+ QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs);
return;
}
loadReference();
@@ -358,7 +362,7 @@ public:
p->value = convertElementToValue(engine(), d()->container->at(*index));
return;
}
- QV4::Object::advanceIterator(this, it, name, index, p, attrs);
+ QV4::Object::virtualAdvanceIterator(this, it, name, index, p, attrs);
}
bool containerDeleteIndexedProperty(uint index)
@@ -366,6 +370,8 @@ public:
/* Qt containers have int (rather than uint) allowable indexes. */
if (index > INT_MAX)
return false;
+ if (d()->isReadOnly)
+ return false;
if (d()->isReference) {
if (!d()->object)
return false;
@@ -432,11 +438,13 @@ public:
const QV4::Value *m_compareFn;
};
- void sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
+ if (d()->isReadOnly)
+ return false;
if (d()->isReference) {
if (!d()->object)
- return;
+ return false;
loadReference();
}
@@ -450,6 +458,8 @@ public:
if (d()->isReference)
storeReference();
+
+ return true;
}
static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -480,6 +490,10 @@ public:
generateWarning(scope.engine, QLatin1String("Index out of range during length set"));
RETURN_UNDEFINED();
}
+
+ if (This->d()->isReadOnly)
+ THROW_TYPE_ERROR();
+
/* Read the sequence from the QObject property if we're a reference */
if (This->d()->isReference) {
if (!This->d()->object)
@@ -524,7 +538,7 @@ public:
quint32 length = array->getLength();
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < length; ++i)
- result.push_back(convertValueToElement<typename Container::value_type>((v = array->getIndexed(i))));
+ result.push_back(convertValueToElement<typename Container::value_type>((v = array->get(i))));
return QVariant::fromValue(result);
}
@@ -549,17 +563,31 @@ public:
QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a);
}
- static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty)
- { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); }
- static bool putIndexed(Managed *that, uint index, const QV4::Value &value)
- { return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); }
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty)
+ {
+ if (!id.isArrayIndex())
+ return Object::virtualGet(that, id, receiver, hasProperty);
+ return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty);
+ }
+ static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver)
+ {
+ if (id.isArrayIndex())
+ return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(id.asArrayIndex(), value);
+ return Object::virtualPut(that, id, value, receiver);
+ }
static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index)
{ return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); }
- static bool deleteIndexedProperty(QV4::Managed *that, uint index)
- { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); }
- static bool isEqualTo(Managed *that, Managed *other)
+ static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id)
+ {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index);
+ }
+ return Object::virtualDeleteProperty(that, id);
+ }
+ static bool virtualIsEqualTo(Managed *that, Managed *other)
{ return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); }
- static void advanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
+ static void virtualAdvanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
{ return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, p, attrs); }
};
@@ -572,6 +600,7 @@ void Heap::QQmlSequence<Container>::init(const Container &container)
this->container = new Container(container);
propertyIndex = -1;
isReference = false;
+ isReadOnly = false;
object.init();
QV4::Scope scope(internalClass->engine);
@@ -581,12 +610,13 @@ void Heap::QQmlSequence<Container>::init(const Container &container)
}
template <typename Container>
-void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex)
+void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex, bool readOnly)
{
Object::init();
this->container = new Container;
this->propertyIndex = propertyIndex;
isReference = true;
+ this->isReadOnly = readOnly;
this->object.init(object);
QV4::Scope scope(internalClass->engine);
QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this);
@@ -668,7 +698,8 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu
#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \
- s->sort(b, thisObject, argv, argc); \
+ if (!s->sort(b, thisObject, argv, argc)) \
+ THROW_TYPE_ERROR(); \
} else
FOREACH_QML_SEQUENCE_TYPE(CALL_SORT)
@@ -691,11 +722,11 @@ bool SequencePrototype::isSequenceType(int sequenceTypeId)
#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
- QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(object, propertyIndex)); \
+ QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(object, propertyIndex, readOnly)); \
return obj.asReturnedValue(); \
} else
-ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded)
+ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool readOnly, bool *succeeded)
{
QV4::Scope scope(engine);
// This function is called when the property is a QObject Q_PROPERTY of
@@ -709,7 +740,7 @@ ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int s
#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
- QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(v.value<SequenceType >())); \
+ QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(v.value<SequenceType >())); \
return obj.asReturnedValue(); \
} else
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index e9bef2f604..da71215bed 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -59,6 +59,8 @@
#include "qv4context_p.h"
#include "qv4string_p.h"
+QT_REQUIRE_CONFIG(qml_sequence_object);
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -72,7 +74,7 @@ struct SequencePrototype : public QV4::Object
static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static bool isSequenceType(int sequenceTypeId);
- static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded);
+ static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool readOnly, bool *succeeded);
static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded);
static int metaTypeForSequence(const Object *object);
static QVariant toVariant(Object *object);
diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp
index 14def49d0a..e151966306 100644
--- a/src/qml/jsruntime/qv4serialize.cpp
+++ b/src/qml/jsruntime/qv4serialize.cpp
@@ -40,13 +40,17 @@
#include "qv4serialize_p.h"
#include <private/qv8engine_p.h>
+#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
#include <private/qqmllistmodelworkeragent_p.h>
+#endif
#include <private/qv4value_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4regexpobject_p.h>
+#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
+#endif
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -82,8 +86,12 @@ enum Type {
WorkerNumber,
WorkerDate,
WorkerRegexp,
+#if QT_CONFIG(qml_list_model)
WorkerListModel,
+#endif
+#if QT_CONFIG(qml_sequence_object)
WorkerSequence
+#endif
};
static inline quint32 valueheader(Type type, quint32 size = 0)
@@ -189,7 +197,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
push(data, valueheader(WorkerArray, length));
ScopedValue val(scope);
for (uint ii = 0; ii < length; ++ii)
- serialize(data, (val = array->getIndexed(ii)), engine);
+ serialize(data, (val = array->get(ii)), engine);
} else if (v.isInteger()) {
reserve(data, 2 * sizeof(quint32));
push(data, valueheader(WorkerInt32));
@@ -228,6 +236,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
} else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
// XXX TODO: Generalize passing objects between the main thread and worker scripts so
// that others can trivially plug in their elements.
+#if QT_CONFIG(qml_list_model)
QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object());
if (lm && lm->agent()) {
QQmlListModelWorkerAgent *agent = lm->agent();
@@ -236,9 +245,13 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
push(data, (void *)agent);
return;
}
+#else
+ Q_UNUSED(qobjectWrapper);
+#endif
// No other QObject's are allowed to be sent
push(data, valueheader(WorkerUndefined));
} else if (const Object *o = v.as<Object>()) {
+#if QT_CONFIG(qml_sequence_object)
if (o->isListType()) {
// valid sequence. we generate a length (sequence length + 1 for the sequence type)
uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32();
@@ -252,10 +265,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
serialize(data, QV4::Primitive::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
ScopedValue val(scope);
for (uint ii = 0; ii < seqLength; ++ii)
- serialize(data, (val = o->getIndexed(ii)), engine); // sequence elements
+ serialize(data, (val = o->get(ii)), engine); // sequence elements
return;
}
+#endif
// regular object
QV4::ScopedValue val(scope, v);
@@ -269,7 +283,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
QV4::ScopedValue s(scope);
for (quint32 ii = 0; ii < length; ++ii) {
- s = properties->getIndexed(ii);
+ s = properties->get(ii);
serialize(data, s, engine);
QV4::String *str = s->as<String>();
@@ -318,7 +332,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
ScopedValue v(scope);
for (quint32 ii = 0; ii < size; ++ii) {
v = deserialize(data, engine);
- a->putIndexed(ii, v);
+ a->put(ii, v);
}
return a.asReturnedValue();
}
@@ -353,6 +367,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
data += ALIGN(length * sizeof(quint16));
return Encode(engine->newRegExpObject(pattern, flags));
}
+#if QT_CONFIG(qml_list_model)
case WorkerListModel:
{
void *ptr = popPtr(data);
@@ -369,6 +384,8 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
agent->setEngine(engine);
return rv->asReturnedValue();
}
+#endif
+#if QT_CONFIG(qml_sequence_object)
case WorkerSequence:
{
ScopedValue value(scope);
@@ -387,6 +404,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded);
return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded);
}
+#endif
}
Q_ASSERT(!"Unreachable");
return QV4::Encode::undefined();
diff --git a/src/qml/jsruntime/qv4setiterator.cpp b/src/qml/jsruntime/qv4setiterator.cpp
new file mode 100644
index 0000000000..4681a49bd6
--- /dev/null
+++ b/src/qml/jsruntime/qv4setiterator.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4estable_p.h>
+#include <private/qv4setiterator_p.h>
+#include <private/qv4setobject_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SetIteratorObject);
+
+void SetIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Set Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue SetIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const SetIteratorObject *thisObject = that->as<SetIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not a Set Iterator instance"));
+
+ Scoped<SetObject> s(scope, thisObject->d()->iteratedSet);
+ uint index = thisObject->d()->setNextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ if (!s) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ Value *arguments = scope.alloc(2);
+
+ while (index < s->d()->esTable->size()) {
+ s->d()->esTable->iterate(index, &arguments[0], &arguments[1]);
+ thisObject->d()->setNextIndex = index + 1;
+
+ if (itemKind == KeyValueIteratorKind) {
+ ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, arguments[0]);
+ resultArray->arrayPut(1, arguments[0]); // yes, the key is repeated.
+ resultArray->setArrayLengthUnchecked(2);
+
+ return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
+ }
+
+ return IteratorPrototype::createIterResultObject(scope.engine, arguments[0], false);
+ }
+
+ thisObject->d()->iteratedSet.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+}
+
diff --git a/src/qml/jsruntime/qv4setiterator_p.h b/src/qml/jsruntime/qv4setiterator_p.h
new file mode 100644
index 0000000000..78eda6d57b
--- /dev/null
+++ b/src/qml/jsruntime/qv4setiterator_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SETITERATOR_P_H
+#define QV4SETITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4iterator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define SetIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedSet) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, setNextIndex)
+
+DECLARE_HEAP_OBJECT(SetIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(SetIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedSet.set(engine, obj);
+ this->setNextIndex = 0;
+ }
+};
+
+}
+
+struct SetIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SetIteratorObject : Object
+{
+ V4_OBJECT2(SetIteratorObject, Object)
+ Q_MANAGED_TYPE(SetIteratorObject)
+ V4_PROTOTYPE(setIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4SETITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp
new file mode 100644
index 0000000000..30e849bfed
--- /dev/null
+++ b/src/qml/jsruntime/qv4setobject.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qv4setobject_p.h"
+#include "qv4setiterator_p.h"
+#include "qv4estable_p.h"
+#include "qv4symbol_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SetCtor);
+DEFINE_OBJECT_VTABLE(SetObject);
+
+void Heap::SetCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Set"));
+}
+
+ReturnedValue SetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ Scope scope(f);
+ Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>());
+
+ if (argc > 0) {
+ ScopedValue iterable(scope, argv[0]);
+ if (!iterable->isUndefined() && !iterable->isNull()) {
+ ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add")))));
+ if (!adder)
+ return scope.engine->throwTypeError();
+ ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+ CHECK_EXCEPTION();
+ if (!iter)
+ return a.asReturnedValue();
+
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+ forever {
+ done = Runtime::method_iteratorNext(scope.engine, iter, nextValue);
+ CHECK_EXCEPTION();
+ if (done->toBoolean())
+ return a.asReturnedValue();
+
+ adder->call(a, nextValue, 1);
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ }
+ }
+ }
+ }
+
+ return a.asReturnedValue();
+}
+
+ReturnedValue SetCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ return scope.engine->throwTypeError(QString::fromLatin1("Set requires new"));
+}
+
+void SetPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ ctor->addSymbolSpecies();
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("add"), method_add, 1);
+ defineDefaultProperty(QStringLiteral("clear"), method_clear, 0);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
+ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr);
+
+ // Per the spec, the value for 'keys' is the same as 'values'.
+ ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("values")));
+ ScopedFunctionObject valuesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, SetPrototype::method_values, 0));
+ defineDefaultProperty(QStringLiteral("keys"), valuesFn);
+ defineDefaultProperty(QStringLiteral("values"), valuesFn);
+
+ defineDefaultProperty(engine->symbol_iterator(), valuesFn);
+
+ ScopedString val(scope, engine->newString(QLatin1String("Set")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+void Heap::SetObject::init()
+{
+ Object::init();
+ esTable = new ESTable();
+}
+
+void Heap::SetObject::destroy()
+{
+ delete esTable;
+ esTable = 0;
+}
+
+void Heap::SetObject::markObjects(Heap::Base *that, MarkStack *markStack)
+{
+ SetObject *s = static_cast<SetObject *>(that);
+ s->esTable->markObjects(markStack);
+ Object::markObjects(that, markStack);
+}
+
+ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argv[0], Primitive::undefinedValue());
+ return that.asReturnedValue();
+}
+
+ReturnedValue SetPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->clear();
+ return Encode::undefined();
+}
+
+ReturnedValue SetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->remove(argv[0]));
+}
+
+ReturnedValue SetPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue SetPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject callbackfn(scope, argv[0]);
+ if (!callbackfn)
+ return scope.engine->throwTypeError();
+
+ ScopedValue thisArg(scope, Primitive::undefinedValue());
+ if (argc > 1)
+ thisArg = ScopedValue(scope, argv[1]);
+
+ Value *arguments = scope.alloc(3);
+ for (uint i = 0; i < that->d()->esTable->size(); ++i) {
+ that->d()->esTable->iterate(i, &arguments[0], &arguments[1]); // fill in key (0), value (1)
+ arguments[1] = arguments[0]; // but for set, we want to return the key twice; value is always undefined.
+
+ arguments[2] = that;
+ callbackfn->call(thisArg, arguments, 3);
+ CHECK_EXCEPTION();
+ }
+ return Encode::undefined();
+}
+
+ReturnedValue SetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->has(argv[0]));
+}
+
+ReturnedValue SetPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->size());
+}
+
+ReturnedValue SetPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4setobject_p.h b/src/qml/jsruntime/qv4setobject_p.h
new file mode 100644
index 0000000000..34649c3f01
--- /dev/null
+++ b/src/qml/jsruntime/qv4setobject_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SETOBJECT_P_H
+#define QV4SETOBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+class ESTable;
+
+namespace Heap {
+
+struct SetCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct SetObject : Object {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
+ void init();
+ void destroy();
+ ESTable *esTable;
+};
+
+}
+
+struct SetCtor: FunctionObject
+{
+ V4_OBJECT2(SetCtor, FunctionObject)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SetObject : Object
+{
+ V4_OBJECT2(SetObject, Object)
+ V4_PROTOTYPE(setPrototype)
+ V4_NEEDS_DESTROY
+};
+
+struct SetPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+} // namespace QV4
+
+
+QT_END_NAMESPACE
+
+#endif // QV4SETOBJECT_P_H
diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp
index 2a3e28bf63..8930c9a94d 100644
--- a/src/qml/jsruntime/qv4sparsearray.cpp
+++ b/src/qml/jsruntime/qv4sparsearray.cpp
@@ -395,7 +395,7 @@ void SparseArray::freeTree(SparseArrayNode *root, int alignment)
SparseArray::SparseArray()
: numEntries(0)
{
- freeList = Primitive::emptyValue(UINT_MAX).asReturnedValue();
+ freeList = Encode(-1);
header.p = 0;
header.left = nullptr;
header.right = nullptr;
diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h
index 51869b259f..c1e50c8dcf 100644
--- a/src/qml/jsruntime/qv4sparsearray_p.h
+++ b/src/qml/jsruntime/qv4sparsearray_p.h
@@ -52,6 +52,7 @@
//
#include "qv4global_p.h"
+#include "qv4value_p.h"
#include <QtCore/qlist.h>
//#define Q_MAP_DEBUG
@@ -151,7 +152,7 @@ struct Q_QML_EXPORT SparseArray
SparseArray(const SparseArray &other);
- ReturnedValue freeList;
+ Value freeList;
private:
SparseArray &operator=(const SparseArray &other);
diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp
new file mode 100644
index 0000000000..a716c53aea
--- /dev/null
+++ b/src/qml/jsruntime/qv4stackframe.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qv4stackframe_p.h"
+#include <QtCore/qstring.h>
+
+using namespace QV4;
+
+QString CppStackFrame::source() const
+{
+ return v4Function ? v4Function->sourceFile() : QString();
+}
+
+QString CppStackFrame::function() const
+{
+ return v4Function ? v4Function->name()->toQString() : QString();
+}
+
+int CppStackFrame::lineNumber() const
+{
+ if (!v4Function)
+ return -1;
+
+ auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
+ return entry.codeOffset < offset;
+ };
+
+ const QV4::CompiledData::Function *cf = v4Function->compiledFunction;
+ uint offset = instructionPointer;
+ const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable();
+ uint nLineNumbers = cf->nLineNumbers;
+ const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1;
+ return line->line;
+}
+
+ReturnedValue CppStackFrame::thisObject() const {
+ return jsFrame->thisObject.asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
new file mode 100644
index 0000000000..aa507d61a6
--- /dev/null
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4STACKFRAME_H
+#define QV4STACKFRAME_H
+
+#include <private/qv4context_p.h>
+#include <private/qv4enginebase_p.h>
+#ifndef V4_BOOTSTRAP
+#include <private/qv4function_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct CallData
+{
+ enum Offsets {
+ Function = 0,
+ Context = 1,
+ Accumulator = 2,
+ This = 3,
+ NewTarget = 4,
+ Argc = 5,
+
+ LastOffset = Argc,
+ OffsetCount = LastOffset + 1
+ };
+
+ Value function;
+ Value context;
+ Value accumulator;
+ Value thisObject;
+ Value newTarget;
+ Value _argc;
+
+ int argc() const {
+ Q_ASSERT(_argc.isInteger());
+ return _argc.int_32();
+ }
+
+ void setArgc(int argc) {
+ Q_ASSERT(argc >= 0);
+ _argc.setInt_32(argc);
+ }
+
+ inline ReturnedValue argument(int i) const {
+ return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue();
+ }
+
+ Value args[1];
+
+ static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); }
+};
+
+Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
+Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value));
+
+struct Q_QML_EXPORT CppStackFrame {
+ EngineBase *engine;
+ Value *savedStackTop;
+ CppStackFrame *parent;
+ Function *v4Function;
+ CallData *jsFrame;
+ const Value *originalArguments;
+ int originalArgumentsCount;
+ int instructionPointer;
+ const char *yield;
+ const char *unwindHandler;
+ const char *unwindLabel;
+ int unwindLevel;
+
+ void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc) {
+ this->engine = engine;
+
+ this->v4Function = v4Function;
+ originalArguments = argv;
+ originalArgumentsCount = argc;
+ instructionPointer = 0;
+ yield = nullptr;
+ unwindHandler = nullptr;
+ unwindLabel = nullptr;
+ unwindLevel = 0;
+ }
+
+ void push() {
+ parent = engine->currentStackFrame;
+ engine->currentStackFrame = this;
+ savedStackTop = engine->jsStackTop;
+ }
+
+ void pop() {
+ engine->currentStackFrame = parent;
+ engine->jsStackTop = savedStackTop;
+ }
+
+#ifndef V4_BOOTSTRAP
+ static uint requiredJSStackFrameSize(uint nRegisters) {
+ return CallData::HeaderSize() + nRegisters;
+ }
+ static uint requiredJSStackFrameSize(Function *v4Function) {
+ return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters;
+ }
+ uint requiredJSStackFrameSize() const {
+ return requiredJSStackFrameSize(v4Function);
+ }
+ void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
+ const Value &thisObject, const Value &newTarget = Primitive::undefinedValue()) {
+ setupJSFrame(stackSpace, function, scope, thisObject, newTarget,
+ v4Function->nFormals, v4Function->compiledFunction->nRegisters);
+ }
+ void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
+ const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters)
+ {
+ jsFrame = reinterpret_cast<CallData *>(stackSpace);
+ jsFrame->function = function;
+ jsFrame->context = scope->asReturnedValue();
+ jsFrame->accumulator = Encode::undefined();
+ jsFrame->thisObject = thisObject;
+ jsFrame->newTarget = newTarget;
+
+ uint argc = uint(originalArgumentsCount);
+ if (argc > nFormals)
+ argc = nFormals;
+ jsFrame->setArgc(argc);
+
+ memcpy(jsFrame->args, originalArguments, argc*sizeof(Value));
+ const Value *end = jsFrame->args + nRegisters;
+ for (Value *v = jsFrame->args + argc; v < end; ++v)
+ *v = Encode::undefined();
+ }
+#endif
+
+ QString source() const;
+ QString function() const;
+ inline QV4::ExecutionContext *context() const {
+ return static_cast<ExecutionContext *>(&jsFrame->context);
+ }
+ int lineNumber() const;
+
+ inline QV4::Heap::CallContext *callContext() const {
+ Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\
+ while (ctx->type != Heap::ExecutionContext::Type_CallContext)
+ ctx = ctx->outer;
+ return static_cast<Heap::CallContext *>(ctx);
+ }
+ ReturnedValue thisObject() const;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index 447992ebec..911103c05d 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -52,8 +52,17 @@ using namespace QV4;
#ifndef V4_BOOTSTRAP
+void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack)
+{
+ StringOrSymbol *s = static_cast<StringOrSymbol *>(that);
+ Heap::StringOrSymbol *id = s->identifier.asStringOrSymbol();
+ if (id)
+ id->mark(markStack);
+}
+
void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
{
+ StringOrSymbol::markObjects(that, markStack);
String *s = static_cast<String *>(that);
if (s->subtype < StringType_Complex)
return;
@@ -68,15 +77,16 @@ void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
}
}
+DEFINE_MANAGED_VTABLE(StringOrSymbol);
DEFINE_MANAGED_VTABLE(String);
-bool String::isEqualTo(Managed *t, Managed *o)
+bool String::virtualIsEqualTo(Managed *t, Managed *o)
{
if (t == o)
return true;
- if (!o->d()->vtable()->isString)
+ if (!o->vtable()->isString)
return false;
return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o));
@@ -128,7 +138,8 @@ void Heap::ComplexString::init(Heap::String *ref, int from, int len)
this->len = len;
}
-void Heap::String::destroy() {
+void Heap::StringOrSymbol::destroy()
+{
if (text) {
internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar));
if (!text->ref.deref())
@@ -155,12 +166,12 @@ uint String::toUInt(bool *ok) const
return UINT_MAX;
}
-void String::makeIdentifierImpl() const
+void String::createPropertyKeyImpl() const
{
if (!d()->text)
d()->simplifyString();
Q_ASSERT(d()->text);
- engine()->identifierTable->identifier(this);
+ engine()->identifierTable->asPropertyKey(this);
}
void Heap::String::simplifyString() const
@@ -174,7 +185,7 @@ void Heap::String::simplifyString() const
text = result.data_ptr();
text->ref.ref();
const ComplexString *cs = static_cast<const ComplexString *>(this);
- identifier = nullptr;
+ identifier = PropertyKey::invalid();
cs->left = cs->right = nullptr;
internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar));
@@ -227,17 +238,26 @@ void Heap::String::append(const String *data, QChar *ch)
}
}
-void Heap::String::createHashValue() const
+void Heap::StringOrSymbol::createHashValue() const
{
- if (!text)
- simplifyString();
+ if (!text) {
+ Q_ASSERT(internalClass->vtable->isString);
+ static_cast<const Heap::String *>(this)->simplifyString();
+ }
Q_ASSERT(text);
const QChar *ch = reinterpret_cast<const QChar *>(text->data());
const QChar *end = ch + text->size;
stringHash = QV4::String::calculateHashValue(ch, end, &subtype);
}
-uint String::getLength(const Managed *m)
+PropertyKey StringOrSymbol::toPropertyKey() const {
+ if (d()->identifier.isValid())
+ return d()->identifier;
+ createPropertyKey();
+ return propertyKey();
+}
+
+qint64 String::virtualGetLength(const Managed *m)
{
return static_cast<const String *>(m)->d()->length();
}
@@ -248,4 +268,3 @@ uint String::toArrayIndex(const QString &str)
{
return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length());
}
-
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 5466cc274d..8a4dc08693 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -60,13 +60,14 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct ExecutionEngine;
-struct Identifier;
+struct PropertyKey;
namespace Heap {
-struct Q_QML_PRIVATE_EXPORT String : Base {
- static void markObjects(Heap::Base *that, MarkStack *markStack);
+struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base
+{
enum StringType {
+ StringType_Symbol,
StringType_Regular,
StringType_ArrayIndex,
StringType_Unknown,
@@ -75,13 +76,20 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
StringType_Complex = StringType_AddedString
};
-#ifndef V4_BOOTSTRAP
- void init(const QString &text);
+ mutable QStringData *text;
+ mutable PropertyKey identifier;
+ mutable uint subtype;
+ mutable uint stringHash;
+
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
void destroy();
- void simplifyString() const;
- int length() const;
- std::size_t retainedTextSize() const {
- return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar));
+
+ inline QString toQString() const {
+ if (!text)
+ return QString();
+ QStringDataPtr ptr = { text };
+ text->ref.ref();
+ return QString(ptr);
}
void createHashValue() const;
inline unsigned hashValue() const {
@@ -91,6 +99,22 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
return stringHash;
}
+};
+
+struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
+
+#ifndef V4_BOOTSTRAP
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
+ void init(const QString &text);
+ void simplifyString() const;
+ int length() const;
+ std::size_t retainedTextSize() const {
+ return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar));
+ }
inline QString toQString() const {
if (subtype >= StringType_Complex)
simplifyString();
@@ -104,7 +128,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
if (hashValue() != other->hashValue())
return false;
Q_ASSERT(subtype < StringType_Complex);
- if (identifier && identifier == other->identifier)
+ if (identifier.isValid() && identifier == other->identifier)
return true;
if (subtype == Heap::String::StringType_ArrayIndex && other->subtype == Heap::String::StringType_ArrayIndex)
return true;
@@ -114,10 +138,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
bool startsWithUpper() const;
- mutable QStringData *text;
- mutable Identifier *identifier;
- mutable uint subtype;
- mutable uint stringHash;
private:
static void append(const String *data, QChar *ch);
#endif
@@ -146,9 +166,29 @@ int String::length() const {
}
-struct Q_QML_PRIVATE_EXPORT String : public Managed {
+struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed {
#ifndef V4_BOOTSTRAP
- V4_MANAGED(String, Managed)
+ V4_MANAGED(StringOrSymbol, Managed)
+ enum {
+ IsStringOrSymbol = true
+ };
+
+private:
+ inline void createPropertyKey() const;
+public:
+ PropertyKey propertyKey() const { Q_ASSERT(d()->identifier.isValid()); return d()->identifier; }
+ PropertyKey toPropertyKey() const;
+
+
+ inline QString toQString() const {
+ return d()->toQString();
+ }
+#endif
+};
+
+struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
+#ifndef V4_BOOTSTRAP
+ V4_MANAGED(String, StringOrSymbol)
Q_MANAGED_TYPE(String)
V4_INTERNALCLASS(String)
V4_NEEDS_DESTROY
@@ -177,24 +217,10 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
inline unsigned hashValue() const {
return d()->hashValue();
}
- uint asArrayIndex() const {
- if (subtype() >= Heap::String::StringType_Unknown)
- d()->createHashValue();
- Q_ASSERT(d()->subtype < Heap::String::StringType_Complex);
- if (subtype() == Heap::String::StringType_ArrayIndex)
- return d()->stringHash;
- return UINT_MAX;
- }
uint toUInt(bool *ok) const;
- void makeIdentifier() const {
- if (d()->identifier)
- return;
- makeIdentifierImpl();
- }
-
// slow path
- Q_NEVER_INLINE void makeIdentifierImpl() const;
+ Q_NEVER_INLINE void createPropertyKeyImpl() const;
static uint createHashValue(const QChar *ch, int length, uint *subtype)
{
@@ -210,11 +236,9 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
bool startsWithUpper() const { return d()->startsWithUpper(); }
- Identifier *identifier() const { return d()->identifier; }
-
protected:
- static bool isEqualTo(Managed *that, Managed *o);
- static uint getLength(const Managed *m);
+ static bool virtualIsEqualTo(Managed *that, Managed *o);
+ static qint64 virtualGetLength(const Managed *m);
#endif
public:
@@ -254,7 +278,7 @@ public:
uint h = toArrayIndex(ch, end);
if (h != UINT_MAX) {
if (subtype)
- *subtype = Heap::String::StringType_ArrayIndex;
+ *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
return h;
}
@@ -264,7 +288,7 @@ public:
}
if (subtype)
- *subtype = Heap::String::StringType_Regular;
+ *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular;
return h;
}
};
@@ -280,9 +304,22 @@ struct ComplexString : String {
}
};
+inline
+void StringOrSymbol::createPropertyKey() const {
+ if (d()->identifier.isValid())
+ return;
+ Q_ASSERT(isString());
+ static_cast<const String *>(this)->createPropertyKeyImpl();
+}
+
+template<>
+inline const StringOrSymbol *Value::as() const {
+ return isManaged() && m()->internalClass->vtable->isStringOrSymbol ? static_cast<const String *>(this) : nullptr;
+}
+
template<>
inline const String *Value::as() const {
- return isManaged() && m()->vtable()->isString ? static_cast<const String *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isString ? static_cast<const String *>(this) : nullptr;
}
template<>
diff --git a/src/qml/jsruntime/qv4stringiterator.cpp b/src/qml/jsruntime/qv4stringiterator.cpp
new file mode 100644
index 0000000000..810ed333e4
--- /dev/null
+++ b/src/qml/jsruntime/qv4stringiterator.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4stringiterator_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(StringIteratorObject);
+
+void StringIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("String Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue StringIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const StringIteratorObject *thisObject = that->as<StringIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not an String Iterator instance"));
+
+ ScopedString s(scope, thisObject->d()->iteratedString);
+ if (!s) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ quint32 index = thisObject->d()->nextIndex;
+
+ QString str = s->toQString();
+ quint32 len = str.length();
+
+ if (index >= len) {
+ thisObject->d()->iteratedString.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ QChar ch = str.at(index);
+ int num = 1;
+ if (ch.unicode() >= 0xd800 && ch.unicode() <= 0xdbff && index + 1 != len) {
+ ch = str.at(index + 1);
+ if (ch.unicode() >= 0xdc00 && ch.unicode() <= 0xdfff)
+ num = 2;
+ }
+
+ thisObject->d()->nextIndex += num;
+
+ ScopedString resultString(scope, scope.engine->newString(s->toQString().mid(index, num)));
+ return IteratorPrototype::createIterResultObject(scope.engine, resultString, false);
+}
+
diff --git a/src/qml/jsruntime/qv4stringiterator_p.h b/src/qml/jsruntime/qv4stringiterator_p.h
new file mode 100644
index 0000000000..672ccc9963
--- /dev/null
+++ b/src/qml/jsruntime/qv4stringiterator_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4STRINGITERATOR_P_H
+#define QV4STRINGITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+#define StringIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, String *, iteratedString) \
+ Member(class, NoMark, quint32, nextIndex)
+
+DECLARE_HEAP_OBJECT(StringIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(StringIteratorObject);
+ void init(String *str, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedString.set(engine, str);
+ this->nextIndex = 0;
+ }
+};
+
+}
+
+struct StringIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct StringIteratorObject : Object
+{
+ V4_OBJECT2(StringIteratorObject, Object)
+ Q_MANAGED_TYPE(StringIteratorObject)
+ V4_PROTOTYPE(stringIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 61176b3706..f2d0d52013 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -44,8 +44,10 @@
#include "qv4objectproto_p.h"
#include <private/qv4mm_p.h>
#include "qv4scopedvalue_p.h"
+#include "qv4symbol_p.h"
#include "qv4alloca_p.h"
#include "qv4jscall_p.h"
+#include "qv4stringiterator_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
@@ -95,19 +97,19 @@ uint Heap::StringObject::length() const
return string->length();
}
-bool StringObject::deleteIndexedProperty(Managed *m, uint index)
+bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id)
{
- ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine();
- Scope scope(v4);
- Scoped<StringObject> o(scope, m->as<StringObject>());
- Q_ASSERT(!!o);
-
- if (index < static_cast<uint>(o->d()->string->toQString().length()))
- return false;
- return true;
+ Q_ASSERT(m->as<StringObject>());
+ if (id.isArrayIndex()) {
+ StringObject *o = static_cast<StringObject *>(m);
+ uint index = id.asArrayIndex();
+ if (index < static_cast<uint>(o->d()->string->toQString().length()))
+ return false;
+ }
+ return Object::virtualDeleteProperty(m, id);
}
-void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
+void StringObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
{
name->setM(nullptr);
StringObject *s = static_cast<StringObject *>(m);
@@ -116,9 +118,8 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name,
while (it->arrayIndex < slen) {
*index = it->arrayIndex;
++it->arrayIndex;
- PropertyAttributes a;
Property pd;
- s->getOwnProperty(*index, &a, &pd);
+ PropertyAttributes a = s->getOwnProperty(PropertyKey::fromArrayIndex(*index), &pd);
if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
*attrs = a;
p->copy(&pd, a);
@@ -133,7 +134,27 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name,
}
}
- return Object::advanceIterator(m, it, name, index, p, attrs);
+ return Object::virtualAdvanceIterator(m, it, name, index, p, attrs);
+}
+
+PropertyAttributes StringObject::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
+{
+ PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
+ if (attributes != Attr_Invalid)
+ return attributes;
+
+ Object *o = static_cast<Object *>(m);
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ if (o->isStringObject()) {
+ if (index >= static_cast<const StringObject *>(m)->length())
+ return Attr_Invalid;
+ if (p)
+ p->value = static_cast<StringObject *>(o)->getIndex(index);
+ return Attr_NotConfigurable|Attr_NotWritable;
+ }
+ }
+ return Attr_Invalid;
}
DEFINE_OBJECT_VTABLE(StringCtor);
@@ -143,7 +164,7 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("String"));
}
-ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
ExecutionEngine *v4 = static_cast<const Object *>(f)->engine();
Scope scope(v4);
@@ -152,16 +173,58 @@ ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value
value = argv[0].toString(v4);
else
value = v4->newString();
+ CHECK_EXCEPTION();
return Encode(v4->newStringObject(value));
}
-ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc)
+ReturnedValue StringCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc)
{
ExecutionEngine *v4 = m->engine();
- if (argc)
- return argv[0].toString(v4)->asReturnedValue();
- else
+ if (!argc)
return v4->newString()->asReturnedValue();
+ if (argv[0].isSymbol())
+ return v4->newString(argv[0].symbolValue()->descriptiveString())->asReturnedValue();
+ return argv[0].toString(v4)->asReturnedValue();
+}
+
+ReturnedValue StringCtor::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
+{
+ QString str(argc, Qt::Uninitialized);
+ QChar *ch = str.data();
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ *ch = QChar(argv[i].toUInt16());
+ ++ch;
+ }
+ *ch = 0;
+ return Encode(b->engine()->newString(str));
+}
+
+
+
+ReturnedValue StringCtor::method_fromCodePoint(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ QString result(argc*2, Qt::Uninitialized); // assume worst case
+ QChar *ch = result.data();
+ for (int i = 0; i < argc; ++i) {
+ double num = argv[i].toNumber();
+ if (e->hasException)
+ return Encode::undefined();
+ int cp = static_cast<int>(num);
+ if (cp != num || cp < 0 || cp > 0x10ffff)
+ return e->throwRangeError(QStringLiteral("String.fromCodePoint: argument out of range."));
+ if (cp > 0xffff) {
+ *ch = QChar::highSurrogate(cp);
+ ++ch;
+ *ch = QChar::lowSurrogate(cp);
+ } else {
+ *ch = cp;
+ }
+ ++ch;
+ }
+ *ch = 0;
+ result.truncate(ch - result.constData());
+ return e->newString(result)->asReturnedValue();
}
void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -169,15 +232,23 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedObject o(scope);
+ // need to set this once again, as these were not fully defined when creating the string proto
+ Heap::InternalClass *ic = scope.engine->classes[ExecutionEngine::Class_StringObject]->changePrototype(scope.engine->objectPrototype()->d());
+ d()->internalClass.set(scope.engine, ic);
+ d()->string.set(scope.engine, scope.engine->id_empty()->d());
+ setProperty(scope.engine, Heap::StringObject::LengthPropertyIndex, Primitive::fromInt32(0));
+
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
- ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), StringCtor::method_fromCharCode, 1);
+ ctor->defineDefaultProperty(QStringLiteral("fromCodePoint"), StringCtor::method_fromCodePoint, 1);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString);
defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical
defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1);
defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1);
+ defineDefaultProperty(QStringLiteral("codePointAt"), method_codePointAt, 1);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1);
defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
@@ -185,6 +256,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1);
defineDefaultProperty(QStringLiteral("match"), method_match, 1);
+ defineDefaultProperty(QStringLiteral("normalize"), method_normalize, 0);
+ defineDefaultProperty(QStringLiteral("padEnd"), method_padEnd, 1);
+ defineDefaultProperty(QStringLiteral("padStart"), method_padStart, 1);
defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1);
defineDefaultProperty(QStringLiteral("replace"), method_replace, 2);
defineDefaultProperty(QStringLiteral("search"), method_search, 1);
@@ -198,6 +272,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase);
defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
defineDefaultProperty(QStringLiteral("trim"), method_trim);
+ defineDefaultProperty(engine->symbol_iterator(), method_iterator);
}
static Heap::String *thisAsString(ExecutionEngine *v4, const QV4::Value *thisObject)
@@ -270,6 +345,29 @@ ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const
return Encode(qt_qnan());
}
+ReturnedValue StringPrototype::method_codePointAt(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+
+ int index = argc ? argv[0].toInteger() : 0;
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+
+ if (index < 0 || index >= value.size())
+ return Encode::undefined();
+
+ uint first = value.at(index).unicode();
+ if (QChar::isHighSurrogate(first) && index + 1 < value.size()) {
+ uint second = value.at(index + 1).unicode();
+ if (QChar::isLowSurrogate(second))
+ return Encode(QChar::surrogateToUcs4(first, second));
+ }
+ return Encode(first);
+}
+
ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *v4 = b->engine();
@@ -298,12 +396,11 @@ ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Va
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = value.length();
if (argc > 1)
@@ -323,9 +420,9 @@ ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Val
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc)
- searchString = argv[0].toQString();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1)
@@ -345,12 +442,11 @@ ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Va
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1) {
@@ -374,9 +470,9 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc)
- searchString = argv[0].toQString();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf();
if (std::isnan(position))
@@ -418,7 +514,7 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value
Scoped<RegExpObject> that(scope, argc ? argv[0] : Primitive::undefinedValue());
if (!that) {
// convert args[0] to a regexp
- that = RegExpCtor::callAsConstructor(b, argv, argc);
+ that = RegExpCtor::virtualCallAsConstructor(b, argv, argc, b);
if (v4->hasException)
return Encode::undefined();
}
@@ -455,6 +551,115 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value
return a.asReturnedValue();
}
+ReturnedValue StringPrototype::method_normalize(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return Encode::undefined();
+
+ QString::NormalizationForm form = QString::NormalizationForm_C;
+ if (argc >= 1 && !argv[0].isUndefined()) {
+ QString f = argv[0].toQString();
+ if (v4->hasException)
+ return Encode::undefined();
+ if (f == QLatin1String("NFC"))
+ form = QString::NormalizationForm_C;
+ else if (f == QLatin1String("NFD"))
+ form = QString::NormalizationForm_D;
+ else if (f == QLatin1String("NFKC"))
+ form = QString::NormalizationForm_KC;
+ else if (f == QLatin1String("NFKD"))
+ form = QString::NormalizationForm_KD;
+ else
+ return v4->throwRangeError(QLatin1String("String.prototype.normalize: Invalid normalization form."));
+ }
+ QString normalized = value.normalized(form);
+ return v4->newString(normalized)->asReturnedValue();
+}
+
+ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ if (thisObject->isNullOrUndefined())
+ return v4->throwTypeError();
+
+ Scope scope(v4);
+ ScopedString s(scope, thisAsString(v4, thisObject));
+ if (v4->hasException)
+ return Encode::undefined();
+ if (!argc)
+ return s->asReturnedValue();
+
+ int maxLen = argv[0].toInteger();
+ if (maxLen <= s->d()->length())
+ return s->asReturnedValue();
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ if (v4->hasException)
+ return Encode::undefined();
+
+ if (fillString.isEmpty())
+ return s->asReturnedValue();
+
+ QString padded = s->toQString();
+ int oldLength = padded.length();
+ int toFill = maxLen - oldLength;
+ padded.resize(maxLen);
+ QChar *ch = padded.data() + oldLength;
+ while (toFill) {
+ int copy = qMin(fillString.length(), toFill);
+ memcpy(ch, fillString.constData(), copy*sizeof(QChar));
+ toFill -= copy;
+ ch += copy;
+ }
+ *ch = 0;
+
+ return v4->newString(padded)->asReturnedValue();
+}
+
+ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ if (thisObject->isNullOrUndefined())
+ return v4->throwTypeError();
+
+ Scope scope(v4);
+ ScopedString s(scope, thisAsString(v4, thisObject));
+ if (v4->hasException)
+ return Encode::undefined();
+ if (!argc)
+ return s->asReturnedValue();
+
+ int maxLen = argv[0].toInteger();
+ if (maxLen <= s->d()->length())
+ return s->asReturnedValue();
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ if (v4->hasException)
+ return Encode::undefined();
+
+ if (fillString.isEmpty())
+ return s->asReturnedValue();
+
+ QString original = s->toQString();
+ int oldLength = original.length();
+ int toFill = maxLen - oldLength;
+ QString padded;
+ padded.resize(maxLen);
+ QChar *ch = padded.data();
+ while (toFill) {
+ int copy = qMin(fillString.length(), toFill);
+ memcpy(ch, fillString.constData(), copy*sizeof(QChar));
+ toFill -= copy;
+ ch += copy;
+ }
+ memcpy(ch, original.constData(), oldLength*sizeof(QChar));
+ ch += oldLength;
+ *ch = 0;
+
+ return v4->newString(padded)->asReturnedValue();
+}
+
+
ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *v4 = b->engine();
@@ -777,12 +982,11 @@ ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1)
@@ -892,17 +1096,6 @@ ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b,
return method_toUpperCase(b, thisObject, argv, argc);
}
-ReturnedValue StringPrototype::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
-{
- QString str(argc, Qt::Uninitialized);
- QChar *ch = str.data();
- for (int i = 0, ei = argc; i < ei; ++i) {
- *ch = QChar(argv[i].toUInt16());
- ++ch;
- }
- return Encode(b->engine()->newString(str));
-}
-
ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
@@ -923,3 +1116,16 @@ ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value
return Encode(v4->newString(QString(chars + start, end - start + 1)));
}
+
+
+
+ReturnedValue StringPrototype::method_iterator(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedString s(scope, thisObject->toString(scope.engine));
+ if (!s || thisObject->isNullOrUndefined())
+ return scope.engine->throwTypeError();
+
+ Scoped<StringIteratorObject> si(scope, scope.engine->memoryManager->allocate<StringIteratorObject>(s->d(), scope.engine));
+ return si->asReturnedValue();
+}
diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h
index 7d25678b61..2d37e36b34 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -70,6 +70,8 @@ DECLARE_HEAP_OBJECT(StringObject, Object) {
LengthPropertyIndex = 0
};
+ void init(bool /*don't init*/)
+ { Object::init(); }
void init();
void init(const QV4::String *string);
@@ -96,18 +98,22 @@ struct StringObject: Object {
return d()->length();
}
- static bool deleteIndexedProperty(Managed *m, uint index);
-
+ using Object::getOwnProperty;
protected:
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs);
+ static bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs);
+ static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
};
struct StringCtor: FunctionObject
{
V4_OBJECT2(StringCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fromCodePoint(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct StringPrototype: StringObject
@@ -118,6 +124,7 @@ struct StringPrototype: StringObject
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_charAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_charCodeAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_codePointAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_endsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -125,6 +132,9 @@ struct StringPrototype: StringObject
static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_localeCompare(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_normalize(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_repeat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -137,8 +147,8 @@ struct StringPrototype: StringObject
static ReturnedValue method_toLocaleLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_trim(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_iterator(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp
new file mode 100644
index 0000000000..bdefe1eb9e
--- /dev/null
+++ b/src/qml/jsruntime/qv4symbol.cpp
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qv4symbol_p.h>
+#include <qv4functionobject_p.h>
+#include <qv4identifiertable_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SymbolCtor);
+DEFINE_MANAGED_VTABLE(Symbol);
+DEFINE_OBJECT_VTABLE(SymbolObject);
+
+void Heap::Symbol::init(const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+ identifier = PropertyKey::fromStringOrSymbol(this);
+ QString desc(s);
+ text = desc.data_ptr();
+ text->ref.ref();
+}
+
+void Heap::SymbolCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Symbol"));
+}
+
+void Heap::SymbolObject::init(const QV4::Symbol *s)
+{
+ Object::init();
+ symbol.set(internalClass->engine, s->d());
+}
+
+ReturnedValue QV4::SymbolCtor::virtualCall(const QV4::FunctionObject *f, const QV4::Value *, const QV4::Value *argv, int argc)
+{
+ Scope scope(f);
+ QString desc = QChar::fromLatin1('@');
+ if (argc && !argv[0].isUndefined()) {
+ ScopedString s(scope, argv[0].toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ desc += s->toQString();
+ }
+ return Symbol::create(scope.engine, desc)->asReturnedValue();
+}
+
+ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedValue k(scope, argc ? argv[0]: Primitive::undefinedValue());
+ ScopedString key(scope, k->toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ QString desc = QLatin1Char('@') + key->toQString();
+ return scope.engine->identifierTable->insertSymbol(desc)->asReturnedValue();
+}
+
+ReturnedValue SymbolCtor::method_keyFor(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!argc || !argv[0].isSymbol())
+ return e->throwTypeError(QLatin1String("Symbol.keyFor: Argument is not a symbol."));
+ const Symbol &arg = static_cast<const Symbol &>(argv[0]);
+ Heap::Symbol *s = e->identifierTable->symbolForId(arg.propertyKey());
+ Q_ASSERT(!s || s == arg.d());
+ if (s)
+ return e->newString(arg.toQString().mid((1)))->asReturnedValue();
+ return Encode::undefined();
+}
+
+void SymbolPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedValue v(scope);
+ ctor->defineReadonlyProperty(engine->id_prototype(), (v = this));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+
+ ctor->defineDefaultProperty(QStringLiteral("for"), SymbolCtor::method_for, 1);
+ ctor->defineDefaultProperty(QStringLiteral("keyFor"), SymbolCtor::method_keyFor, 1);
+ ctor->defineReadonlyProperty(QStringLiteral("hasInstance"), *engine->symbol_hasInstance());
+ ctor->defineReadonlyProperty(QStringLiteral("isConcatSpreadable"), *engine->symbol_isConcatSpreadable());
+ ctor->defineReadonlyProperty(QStringLiteral("iterator"), *engine->symbol_iterator());
+ ctor->defineReadonlyProperty(QStringLiteral("match"), *engine->symbol_match());
+ ctor->defineReadonlyProperty(QStringLiteral("replace"), *engine->symbol_replace());
+ ctor->defineReadonlyProperty(QStringLiteral("search"), *engine->symbol_search());
+ ctor->defineReadonlyProperty(QStringLiteral("species"), *engine->symbol_species());
+ ctor->defineReadonlyProperty(QStringLiteral("split"), *engine->symbol_split());
+ ctor->defineReadonlyProperty(QStringLiteral("toPrimitive"), *engine->symbol_toPrimitive());
+ ctor->defineReadonlyProperty(QStringLiteral("toStringTag"), *engine->symbol_toStringTag());
+ ctor->defineReadonlyProperty(QStringLiteral("unscopables"), *engine->symbol_unscopables());
+
+ defineDefaultProperty(QStringLiteral("constructor"), (v = ctor));
+ defineDefaultProperty(QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(QStringLiteral("valueOf"), method_valueOf);
+ defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable);
+
+ v = engine->newString(QStringLiteral("Symbol"));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), v);
+
+}
+
+ReturnedValue SymbolPrototype::method_toString(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<Symbol> s(scope, thisObject->as<Symbol>());
+ if (!s) {
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ s = o->d()->symbol;
+ else
+ return scope.engine->throwTypeError();
+ }
+ return scope.engine->newString(s->descriptiveString())->asReturnedValue();
+}
+
+ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<Symbol> s(scope, thisObject->as<Symbol>());
+ if (!s) {
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ s = o->d()->symbol;
+ else
+ return scope.engine->throwTypeError();
+ }
+ return s->asReturnedValue();
+}
+
+ReturnedValue SymbolPrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ if (thisObject->isSymbol())
+ return thisObject->asReturnedValue();
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ return o->d()->symbol->asReturnedValue();
+ return f->engine()->throwTypeError();
+}
+
+Heap::Symbol *Symbol::create(ExecutionEngine *e, const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+ return e->memoryManager->alloc<Symbol>(s);
+}
+
+QString Symbol::descriptiveString() const
+{
+ return QLatin1String("Symbol(") + toQString().midRef(1) + QLatin1String(")");
+}
diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h
new file mode 100644
index 0000000000..46fa2979f8
--- /dev/null
+++ b/src/qml/jsruntime/qv4symbol_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4_SYMBOL_H
+#define QV4_SYMBOL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4string_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+struct SymbolCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct Symbol : StringOrSymbol {
+ void init(const QString &s);
+};
+
+#define SymbolObjectMembers(class, Member) \
+ Member(class, Pointer, Symbol *, symbol)
+
+DECLARE_HEAP_OBJECT(SymbolObject, Object) {
+ DECLARE_MARKOBJECTS(SymbolObject);
+ void init(const QV4::Symbol *s);
+};
+
+}
+
+struct SymbolCtor : FunctionObject
+{
+ V4_OBJECT2(SymbolCtor, FunctionObject)
+
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_for(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keyFor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SymbolPrototype : Object
+{
+ V4_PROTOTYPE(objectPrototype)
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_symbolToPrimitive(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct Symbol : StringOrSymbol
+{
+ V4_MANAGED(Symbol, StringOrSymbol)
+ Q_MANAGED_TYPE(Symbol)
+ V4_INTERNALCLASS(Symbol)
+ V4_NEEDS_DESTROY
+
+ static Heap::Symbol *create(ExecutionEngine *e, const QString &s);
+
+ QString descriptiveString() const;
+};
+
+struct SymbolObject : Object
+{
+ V4_OBJECT2(SymbolObject, Object)
+ Q_MANAGED_TYPE(SymbolObject)
+ V4_INTERNALCLASS(SymbolObject)
+ V4_PROTOTYPE(symbolPrototype)
+
+ static bool virtualPut(Managed *, PropertyKey, const Value &, Value *) { return false; }
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index ea1532b8ce..cb9cdd8df5 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -36,15 +36,20 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+
#include "qv4typedarray_p.h"
+#include "qv4arrayiterator_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <cmath>
using namespace QV4;
+DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayCtor);
+DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayPrototype);
DEFINE_OBJECT_VTABLE(TypedArrayCtor);
DEFINE_OBJECT_VTABLE(TypedArrayPrototype);
DEFINE_OBJECT_VTABLE(TypedArray);
@@ -209,16 +214,19 @@ void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t
type = t;
}
-ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
Scope scope(f->engine());
const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f);
if (!argc || !argv[0].isObject()) {
// ECMA 6 22.2.1.1
- double l = argc ? argv[0].toNumber() : 0;
+ qint64 l = argc ? argv[0].toIndex() : 0;
if (scope.engine->hasException)
return Encode::undefined();
+ // ### lift UINT_MAX restriction
+ if (l < 0 || l > UINT_MAX)
+ return scope.engine->throwRangeError(QLatin1String("Index out of range."));
uint len = (uint)l;
if (l != len)
scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array."));
@@ -312,7 +320,10 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
return scope.engine->throwTypeError();
uint elementSize = operations[that->d()->type].bytesPerElement;
- Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize));
+ size_t bufferSize;
+ if (mul_overflow(size_t(l), size_t(elementSize), &bufferSize))
+ return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length"));
+ Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize));
if (scope.engine->hasException)
return Encode::undefined();
@@ -325,7 +336,7 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
char *b = newBuffer->d()->data->data();
ScopedValue val(scope);
while (idx < l) {
- val = o->getIndexed(idx);
+ val = o->get(idx);
array->d()->type->write(scope.engine, b, 0, val);
if (scope.engine->hasException)
return Encode::undefined();
@@ -337,9 +348,9 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
return array.asReturnedValue();
}
-ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue TypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
{
- return callAsConstructor(f, argv, argc);
+ return f->engine()->throwTypeError(QStringLiteral("calling a TypedArray constructor without new is invalid"));
}
void Heap::TypedArray::init(Type t)
@@ -351,13 +362,17 @@ void Heap::TypedArray::init(Type t)
Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
{
- QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable());
- ic = ic->changePrototype(e->typedArrayPrototype[t].d());
- return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t);
+ Scope scope(e);
+ Scoped<InternalClass> ic(scope, e->newInternalClass(staticVTable(), e->typedArrayPrototype + t));
+ return e->memoryManager->allocObject<TypedArray>(ic->d(), t);
}
-ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ if (!id.isArrayIndex())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
+ uint index = id.asArrayIndex();
Scope scope(static_cast<const Object *>(m)->engine());
Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m));
@@ -373,8 +388,12 @@ ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProp
return a->d()->type->read(a->d()->buffer->data->data(), byteOffset);
}
-bool TypedArray::putIndexed(Managed *m, uint index, const Value &value)
+bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isArrayIndex())
+ return Object::virtualPut(m, id, value, receiver);
+
+ uint index = id.asArrayIndex();
ExecutionEngine *v4 = static_cast<Object *>(m)->engine();
if (v4->hasException)
return false;
@@ -395,21 +414,18 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3));
- ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3));
+ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
+ ctor->setPrototypeOf(engine->intrinsicTypedArrayCtor());
+
+ setPrototypeOf(engine->intrinsicTypedArrayPrototype());
defineDefaultProperty(engine->id_constructor(), (o = ctor));
- defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
- defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
- defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
- defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
-
- defineDefaultProperty(QStringLiteral("set"), method_set, 1);
- defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0);
}
-ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -419,7 +435,7 @@ ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, co
return v->d()->buffer->asReturnedValue();
}
-ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -429,7 +445,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b
return Encode(v->d()->byteLength);
}
-ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -439,7 +455,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b
return Encode(v->d()->byteOffset);
}
-ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -449,7 +465,43 @@ ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, co
return Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
}
-ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> O(scope, thisObject);
+ if (!O)
+ THROW_TYPE_ERROR();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> O(scope, thisObject);
+ if (!O)
+ THROW_TYPE_ERROR();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> O(scope, thisObject);
+ if (!O)
+ THROW_TYPE_ERROR();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
Scoped<TypedArray> a(scope, *thisObject);
@@ -487,7 +539,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val
char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize;
ScopedValue val(scope);
while (idx < l) {
- val = o->getIndexed(idx);
+ val = o->get(idx);
a->d()->type->write(scope.engine, b, 0, val);
if (scope.engine->hasException)
RETURN_UNDEFINED();
@@ -538,7 +590,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val
RETURN_UNDEFINED();
}
-ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(builtin);
Scoped<TypedArray> a(scope, *thisObject);
@@ -578,3 +630,47 @@ ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin
arguments[2] = Encode(newLen);
return constructor->callAsConstructor(arguments, 3);
}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ const TypedArray *a = thisObject->as<TypedArray>();
+ if (!a)
+ return Encode::undefined();
+
+ return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue IntrinsicTypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor)
+{
+ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ctor->addSymbolSpecies();
+
+ defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
+ defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
+ defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
+ defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
+
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 1);
+ defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0);
+
+ Scope scope(engine);
+ ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
+ ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
+ defineDefaultProperty(QStringLiteral("values"), values);
+ defineDefaultProperty(engine->symbol_iterator(), values);
+
+ defineAccessorProperty(engine->symbol_toStringTag(), method_get_toStringTag, nullptr);
+}
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index 129c662c97..967d3235b2 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -97,12 +97,18 @@ DECLARE_HEAP_OBJECT(TypedArray, Object) {
void init(Type t);
};
+struct IntrinsicTypedArrayCtor : FunctionObject {
+};
+
struct TypedArrayCtor : FunctionObject {
void init(QV4::ExecutionContext *scope, TypedArray::Type t);
TypedArray::Type type;
};
+struct IntrinsicTypedArrayPrototype : Object {
+};
+
struct TypedArrayPrototype : Object {
inline void init(TypedArray::Type t);
TypedArray::Type type;
@@ -132,34 +138,56 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object
Heap::TypedArray::Type arrayType() const {
return static_cast<Heap::TypedArray::Type>(d()->arrayType);
}
+ using Object::get;
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool putIndexed(Managed *m, uint index, const Value &value);
+ 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);
+};
+
+struct IntrinsicTypedArrayCtor: FunctionObject
+{
+ V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct TypedArrayCtor: FunctionObject
{
V4_OBJECT2(TypedArrayCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
-
-struct TypedArrayPrototype : Object
+struct IntrinsicTypedArrayPrototype : Object
{
- V4_OBJECT2(TypedArrayPrototype, Object)
+ V4_OBJECT2(IntrinsicTypedArrayPrototype, Object)
V4_PROTOTYPE(objectPrototype)
- void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
+ void init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor);
static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_subarray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+};
+
+struct TypedArrayPrototype : Object
+{
+ V4_OBJECT2(TypedArrayPrototype, Object)
+ V4_PROTOTYPE(objectPrototype)
+
+ void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
};
inline void
diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h
index 2669a3e4bf..073832937d 100644
--- a/src/qml/jsruntime/qv4util_p.h
+++ b/src/qml/jsruntime/qv4util_p.h
@@ -59,26 +59,6 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-template <typename T>
-struct TemporaryAssignment
-{
- TemporaryAssignment(T &var, const T& temporaryValue)
- : variable(var)
- , savedValue(var)
- {
- variable = temporaryValue;
- }
- ~TemporaryAssignment()
- {
- variable = savedValue;
- }
- T &variable;
- T savedValue;
-private:
- TemporaryAssignment(const TemporaryAssignment<T>&);
- TemporaryAssignment operator=(const TemporaryAssignment<T>&);
-};
-
#if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND)
// Sanity:
class BitVector
diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp
index 0d4711df3c..cbc153bb86 100644
--- a/src/qml/jsruntime/qv4value.cpp
+++ b/src/qml/jsruntime/qv4value.cpp
@@ -39,7 +39,9 @@
#include <qv4engine_p.h>
#include <qv4runtime_p.h>
#include <qv4string_p.h>
+#include <qv4propertykey_p.h>
#ifndef V4_BOOTSTRAP
+#include <qv4symbol_p.h>
#include <qv4object_p.h>
#include <qv4objectproto_p.h>
#include <private/qv4mm_p.h>
@@ -84,7 +86,7 @@ bool Value::toBooleanImpl(Value val)
#ifdef V4_BOOTSTRAP
Q_UNIMPLEMENTED();
#else
- if (b->vtable()->isString)
+ if (b->internalClass->vtable->isString)
return static_cast<Heap::String *>(b)->length() > 0;
#endif
return true;
@@ -107,6 +109,11 @@ double Value::toNumberImpl(Value val)
#else
if (String *s = val.stringValue())
return RuntimeHelpers::stringToNumber(s->toQString());
+ if (val.isSymbol()) {
+ Managed &m = static_cast<Managed &>(val);
+ m.engine()->throwTypeError();
+ return 0;
+ }
{
Q_ASSERT(val.isObject());
Scope scope(val.objectValue()->engine());
@@ -145,6 +152,8 @@ QString Value::toQStringNoThrow() const
case Value::Managed_Type:
if (String *s = stringValue())
return s->toQString();
+ if (Symbol *s = symbolValue())
+ return s->descriptiveString();
{
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
@@ -197,9 +206,12 @@ QString Value::toQString() const
else
return QStringLiteral("false");
case Value::Managed_Type:
- if (String *s = stringValue())
+ if (String *s = stringValue()) {
return s->toQString();
- {
+ } else if (isSymbol()) {
+ static_cast<const Managed *>(this)->engine()->throwTypeError();
+ return QString();
+ } else {
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
@@ -217,6 +229,25 @@ QString Value::toQString() const
}
} // switch
}
+
+QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const
+{
+ if (isInteger() && int_32() >= 0)
+ return PropertyKey::fromArrayIndex(static_cast<uint>(int_32()));
+ if (isStringOrSymbol()) {
+ Scope scope(e);
+ ScopedStringOrSymbol s(scope, this);
+ return s->toPropertyKey();
+ }
+ Scope scope(e);
+ ScopedValue v(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
+ if (!v->isStringOrSymbol())
+ v = v->toString(e);
+ if (e->hasException)
+ return PropertyKey::invalid();
+ ScopedStringOrSymbol s(scope, v);
+ return s->toPropertyKey();
+}
#endif // V4_BOOTSTRAP
bool Value::sameValue(Value other) const {
@@ -235,6 +266,25 @@ bool Value::sameValue(Value other) const {
return false;
}
+bool Value::sameValueZero(Value other) const {
+ if (_val == other._val)
+ return true;
+ String *s = stringValue();
+ String *os = other.stringValue();
+ if (s && os)
+ return s->isEqualTo(os);
+ if (isInteger() && other.isDouble())
+ return double(int_32()) == other.doubleValue();
+ if (isDouble() && other.isInteger())
+ return other.int_32() == doubleValue();
+ if (isDouble() && other.isDouble()) {
+ if (doubleValue() == 0 && other.doubleValue() == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
#ifndef V4_BOOTSTRAP
Heap::String *Value::toString(ExecutionEngine *e, Value val)
{
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index a5ee6b5373..6a6df3eb6d 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -56,6 +56,7 @@
#include <QtCore/QString>
#include "qv4global_p.h"
#include <private/qv4heap_p.h>
+#include <private/qv4internalclass_p.h>
#include <private/qnumeric_p.h>
@@ -184,23 +185,7 @@ public:
QML_NEARLY_ALWAYS_INLINE void setEmpty()
{
- setTagValue(quint32(ValueTypeInternal::Empty), value());
- }
-
- QML_NEARLY_ALWAYS_INLINE void setEmpty(int i)
- {
- setTagValue(quint32(ValueTypeInternal::Empty), quint32(i));
- }
-
- QML_NEARLY_ALWAYS_INLINE void setEmpty(quint32 i)
- {
- setTagValue(quint32(ValueTypeInternal::Empty), i);
- }
-
- QML_NEARLY_ALWAYS_INLINE quint32 emptyValue()
- {
- Q_ASSERT(isEmpty());
- return quint32(value());
+ setTagValue(quint32(ValueTypeInternal::Empty), 0);
}
// ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
@@ -248,7 +233,8 @@ public:
IsManagedOrUndefined_Shift = 64-15,
IsIntegerConvertible_Shift = 64-15,
IsIntegerOrBool_Shift = 64-16,
- QuickType_Shift = 64 - 17
+ QuickType_Shift = 64 - 17,
+ IsPositiveIntShift = 31
};
static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
@@ -315,6 +301,14 @@ public:
}
inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; }
+ inline bool isPositiveInt() const {
+#if QT_POINTER_SIZE == 4
+ return isInteger() && int_32() >= 0;
+#else
+ return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
+#endif
+ }
+
QML_NEARLY_ALWAYS_INLINE double doubleValue() const {
Q_ASSERT(isDouble());
double d;
@@ -331,6 +325,8 @@ public:
Q_ASSERT(isDouble());
}
inline bool isString() const;
+ inline bool isStringOrSymbol() const;
+ inline bool isSymbol() const;
inline bool isObject() const;
inline bool isFunctionObject() const;
inline bool isInt32() {
@@ -338,14 +334,17 @@ public:
return true;
if (isDouble()) {
double d = doubleValue();
- int i = (int)d;
- if (i == d && !(d == 0 && std::signbit(d))) {
- setInt_32(i);
+ if (isInt32(d)) {
+ setInt_32(int(d));
return true;
}
}
return false;
}
+ QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
+ int i = int(d);
+ return (i == d && !(d == 0 && std::signbit(d)));
+ }
double asDouble() const {
if (tag() == quint32(ValueTypeInternal::Integer))
return int_32();
@@ -362,7 +361,17 @@ public:
QML_NEARLY_ALWAYS_INLINE String *stringValue() const {
if (!isString())
return nullptr;
- return reinterpret_cast<String*>(const_cast<Value *>(this));
+ return reinterpret_cast<String *>(const_cast<Value *>(this));
+ }
+ QML_NEARLY_ALWAYS_INLINE StringOrSymbol *stringOrSymbolValue() const {
+ if (!isStringOrSymbol())
+ return nullptr;
+ return reinterpret_cast<StringOrSymbol *>(const_cast<Value *>(this));
+ }
+ QML_NEARLY_ALWAYS_INLINE Symbol *symbolValue() const {
+ if (!isSymbol())
+ return nullptr;
+ return reinterpret_cast<Symbol *>(const_cast<Value *>(this));
}
QML_NEARLY_ALWAYS_INLINE Object *objectValue() const {
if (!isObject())
@@ -388,6 +397,8 @@ public:
int toUInt16() const;
inline int toInt32() const;
inline unsigned int toUInt32() const;
+ qint64 toLength() const;
+ inline qint64 toIndex() const;
bool toBoolean() const {
if (integerCompatible())
@@ -407,6 +418,8 @@ public:
return reinterpret_cast<Heap::String *>(m());
return toString(e, *this);
}
+ QV4::PropertyKey toPropertyKey(ExecutionEngine *e) const;
+
static Heap::String *toString(ExecutionEngine *e, Value val);
Heap::Object *toObject(ExecutionEngine *e) const {
if (isObject())
@@ -428,11 +441,11 @@ public:
if (!isManaged())
return nullptr;
- Q_ASSERT(m()->vtable());
+ Q_ASSERT(m()->internalClass->vtable);
#if !defined(QT_NO_QOBJECT_CHECK)
static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this));
#endif
- const VTable *vt = m()->vtable();
+ const VTable *vt = m()->internalClass->vtable;
while (vt) {
if (vt == T::staticVTable())
return static_cast<const T *>(this);
@@ -455,8 +468,6 @@ public:
return static_cast<const T *>(managed());
}
- inline uint asArrayIndex() const;
- inline bool asArrayIndex(uint &idx) const;
#ifndef V4_BOOTSTRAP
uint asArrayLength(bool *ok) const;
#endif
@@ -465,8 +476,9 @@ public:
ReturnedValue asReturnedValue() const { return _val; }
static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; }
- // Section 9.12
+ // As per ES specs
bool sameValue(Value other) const;
+ bool sameValueZero(Value other) const;
inline void mark(MarkStack *markStack);
@@ -500,18 +512,32 @@ inline void Value::mark(MarkStack *markStack)
inline bool Value::isString() const
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isString;
+ return b && b->internalClass->vtable->isString;
+}
+
+bool Value::isStringOrSymbol() const
+{
+ Heap::Base *b = heapObject();
+ return b && b->internalClass->vtable->isStringOrSymbol;
+}
+
+bool Value::isSymbol() const
+{
+ Heap::Base *b = heapObject();
+ return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString;
}
+
inline bool Value::isObject() const
+
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isObject;
+ return b && b->internalClass->vtable->isObject;
}
inline bool Value::isFunctionObject() const
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isFunctionObject;
+ return b && b->internalClass->vtable->isFunctionObject;
}
inline bool Value::isPrimitive() const
@@ -528,43 +554,6 @@ inline double Value::toNumber() const
return toNumberImpl();
}
-
-#ifndef V4_BOOTSTRAP
-inline uint Value::asArrayIndex() const
-{
-#if QT_POINTER_SIZE == 8
- if (!isNumber())
- return UINT_MAX;
- if (isInteger())
- return int_32() >= 0 ? (uint)int_32() : UINT_MAX;
-#else
- if (isInteger() && int_32() >= 0)
- return (uint)int_32();
- if (!isDouble())
- return UINT_MAX;
-#endif
- double d = doubleValue();
- uint idx = (uint)d;
- if (idx != d)
- return UINT_MAX;
- return idx;
-}
-
-inline bool Value::asArrayIndex(uint &idx) const
-{
- if (Q_LIKELY(!isDouble())) {
- if (Q_LIKELY(isInteger() && int_32() >= 0)) {
- idx = (uint)int_32();
- return true;
- }
- return false;
- }
- double d = doubleValue();
- idx = (uint)d;
- return (idx == d && idx != UINT_MAX);
-}
-#endif
-
inline
ReturnedValue Heap::Base::asReturnedValue() const
{
@@ -576,7 +565,6 @@ ReturnedValue Heap::Base::asReturnedValue() const
struct Q_QML_PRIVATE_EXPORT Primitive : public Value
{
inline static Primitive emptyValue();
- inline static Primitive emptyValue(uint v);
static inline Primitive fromBoolean(bool b);
static inline Primitive fromInt32(int i);
inline static Primitive undefinedValue();
@@ -602,14 +590,7 @@ inline Primitive Primitive::undefinedValue()
inline Primitive Primitive::emptyValue()
{
Primitive v;
- v.setEmpty(0);
- return v;
-}
-
-inline Primitive Primitive::emptyValue(uint e)
-{
- Primitive v;
- v.setEmpty(e);
+ v.setEmpty();
return v;
}
@@ -755,7 +736,7 @@ struct Encode {
}
static ReturnedValue smallestNumber(double d) {
- if (static_cast<int>(d) == d && !(d == 0. && std::signbit(d)))
+ if (Value::isInt32(d))
return Encode(static_cast<int>(d));
else
return Encode(d);
@@ -788,6 +769,31 @@ inline unsigned int Value::toUInt32() const
return static_cast<unsigned int>(toInt32());
}
+inline qint64 Value::toLength() const
+{
+ if (Q_LIKELY(integerCompatible()))
+ return int_32() < 0 ? 0 : int_32();
+ double i = Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl());
+ if (i <= 0)
+ return 0;
+ if (i > (static_cast<qint64>(1) << 53) - 1)
+ return (static_cast<qint64>(1) << 53) - 1;
+ return static_cast<qint64>(i);
+}
+
+inline qint64 Value::toIndex() const
+{
+ qint64 idx;
+ if (Q_LIKELY(integerCompatible())) {
+ idx = int_32();
+ } else {
+ idx = static_cast<qint64>(Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl()));
+ }
+ if (idx > (static_cast<qint64>(1) << 53) - 1)
+ idx = -1;
+ return idx;
+}
+
inline double Value::toInteger() const
{
if (integerCompatible())
@@ -831,7 +837,7 @@ struct ValueArray {
WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue());
}
void set(EngineBase *e, uint index, Heap::Base *b) {
- WriteBarrier::write(e, base(), values[index].data_ptr(), b->asReturnedValue());
+ WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
inline const Value &operator[] (uint index) const {
Q_ASSERT(index < alloc);
@@ -884,7 +890,6 @@ struct ValueArray {
// have wrong offsets between host and target.
Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index bee17e0390..ef0877dbd0 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -70,7 +70,7 @@ bool VariantObject::Data::isScarce() const
return t == QVariant::Pixmap || t == QVariant::Image;
}
-bool VariantObject::isEqualTo(Managed *m, Managed *other)
+bool VariantObject::virtualIsEqualTo(Managed *m, Managed *other)
{
Q_ASSERT(m->as<QV4::VariantObject>());
QV4::VariantObject *lv = static_cast<QV4::VariantObject *>(m);
diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h
index 62fa7ff9a8..78e0a5373a 100644
--- a/src/qml/jsruntime/qv4variantobject_p.h
+++ b/src/qml/jsruntime/qv4variantobject_p.h
@@ -99,7 +99,8 @@ struct VariantObject : Object
void addVmePropertyReference() const;
void removeVmePropertyReference() const;
- static bool isEqualTo(Managed *m, Managed *other);
+protected:
+ static bool virtualIsEqualTo(Managed *m, Managed *other);
};
struct VariantPrototype : VariantObject
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index a1f5b01fa9..53e5632eff 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -55,12 +55,13 @@
#include <private/qv4string_p.h>
#include <private/qv4profiling_p.h>
#include <private/qv4jscall_p.h>
+#include <private/qv4generatorobject_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <iostream>
#include "qv4alloca_p.h"
-#include <private/qv4jit_p.h>
+#include <private/qv4baselinejit_p.h>
#undef COUNT_INSTRUCTIONS
@@ -327,7 +328,7 @@ static struct InstrCount {
#ifdef MOTH_COMPUTED_GOTO
#define MOTH_END_INSTR(instr) \
- MOTH_DISPATCH() \
+ MOTH_DISPATCH_SINGLE() \
}
#else // !MOTH_COMPUTED_GOTO
#define MOTH_END_INSTR(instr) \
@@ -343,7 +344,7 @@ static struct InstrCount {
#endif
#define CHECK_EXCEPTION \
if (engine->hasException) \
- goto catchException
+ goto handleUnwind
static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
{
@@ -361,95 +362,6 @@ static inline const QV4::Value &constant(Function *function, int index)
return function->compilationUnit->constants[index];
}
-
-static bool compareEqual(QV4::Value lhs, QV4::Value rhs)
-{
- redo:
- if (lhs.asReturnedValue() == rhs.asReturnedValue())
- return !lhs.isNaN();
-
- int lt = lhs.quickType();
- int rt = rhs.quickType();
- if (rt < lt) {
- qSwap(lhs, rhs);
- qSwap(lt, rt);
- }
-
- switch (lt) {
- case QV4::Value::QT_ManagedOrUndefined:
- if (lhs.isUndefined())
- return rhs.isNullOrUndefined();
- Q_FALLTHROUGH();
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3:
- // LHS: Managed
- switch (rt) {
- case QV4::Value::QT_ManagedOrUndefined:
- if (rhs.isUndefined())
- return false;
- Q_FALLTHROUGH();
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3: {
- // RHS: Managed
- Heap::Base *l = lhs.m();
- Heap::Base *r = rhs.m();
- Q_ASSERT(l);
- Q_ASSERT(r);
- if (l->vtable()->isString == r->vtable()->isString)
- return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
- if (l->vtable()->isString) {
- rhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT));
- break;
- } else {
- Q_ASSERT(r->vtable()->isString);
- lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
- break;
- }
- return false;
- }
- case QV4::Value::QT_Empty:
- Q_UNREACHABLE();
- case QV4::Value::QT_Null:
- return false;
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- rhs = Primitive::fromDouble(rhs.int_32());
- // fall through
- default: // double
- if (lhs.m()->vtable()->isString)
- return RuntimeHelpers::toNumber(lhs) == rhs.doubleValue();
- else
- lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
- }
- goto redo;
- case QV4::Value::QT_Empty:
- Q_UNREACHABLE();
- case QV4::Value::QT_Null:
- return rhs.isNull();
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- switch (rt) {
- case QV4::Value::QT_ManagedOrUndefined:
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3:
- case QV4::Value::QT_Empty:
- case QV4::Value::QT_Null:
- Q_UNREACHABLE();
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- return lhs.int_32() == rhs.int_32();
- default: // double
- return lhs.int_32() == rhs.doubleValue();
- }
- default: // double
- Q_ASSERT(rhs.isDouble());
- return lhs.doubleValue() == rhs.doubleValue();
- }
-}
-
static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
{
redo:
@@ -462,7 +374,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
case QV4::Value::QT_ManagedOrUndefined2:
case QV4::Value::QT_ManagedOrUndefined3:
// LHS: Managed
- if (lhs.m()->vtable()->isString)
+ if (lhs.m()->internalClass->vtable->isString)
return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs;
accumulator = lhs;
lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT));
@@ -479,7 +391,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
}
}
-#define STORE_IP() frame.instructionPointer = int(code - codeStart);
+#define STORE_IP() frame->instructionPointer = int(code - function->codeData);
#define STORE_ACC() accumulator = acc;
#define ACC Primitive::fromReturnedValue(acc)
#define VALUE_TO_INT(i, val) \
@@ -500,82 +412,46 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
} \
} while (false)
-QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine)
{
qt_v4ResolvePendingBreakpointsHook();
- ExecutionEngine *engine;
- QV4::Value *stack;
- CppStackFrame frame;
- frame.originalArguments = argv;
- frame.originalArgumentsCount = argc;
- Function *function;
-
- {
- Heap::ExecutionContext *scope;
-
- quintptr d = reinterpret_cast<quintptr>(fo);
- if (d & 0x1) {
- // we don't have a FunctionObject, but a ExecData
- ExecData *data = reinterpret_cast<ExecData *>(d - 1);
- function = data->function;
- scope = data->scope->d();
- fo = nullptr;
- } else {
- function = fo->function();
- scope = fo->scope();
- }
-
- engine = function->internalClass->engine;
-
- stack = engine->jsStackTop;
- CallData *callData = reinterpret_cast<CallData *>(stack);
- callData->function = fo ? fo->asReturnedValue() : Encode::undefined();
- callData->context = scope;
- callData->accumulator = Encode::undefined();
- callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue();
- if (argc > int(function->nFormals))
- argc = int(function->nFormals);
- callData->setArgc(argc);
-
- int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters;
- engine->jsStackTop += jsStackFrameSize;
- memcpy(callData->args, argv, argc*sizeof(Value));
- for (Value *v = callData->args + argc; v < engine->jsStackTop; ++v)
- *v = Encode::undefined();
-
- frame.parent = engine->currentStackFrame;
- frame.v4Function = function;
- frame.instructionPointer = 0;
- frame.jsFrame = callData;
- engine->currentStackFrame = &frame;
- }
CHECK_STACK_LIMITS(engine);
+ Function *function = frame->v4Function;
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
QV4::Debugging::Debugger *debugger = engine->debugger();
- const uchar *exceptionHandler = nullptr;
-
- QV4::Value &accumulator = frame.jsFrame->accumulator;
- QV4::ReturnedValue acc = Encode::undefined();
#ifdef V4_ENABLE_JIT
- if (function->jittedCode == nullptr && debugger == nullptr) {
- if (engine->canJIT(function))
- QV4::JIT::BaselineJIT(function).generate();
- else
- ++function->interpreterCallCount;
+ if (debugger == nullptr) {
+ if (function->jittedCode == nullptr) {
+ if (engine->canJIT(function))
+ QV4::JIT::BaselineJIT(function).generate();
+ else
+ ++function->interpreterCallCount;
+ }
+ if (function->jittedCode != nullptr)
+ return function->jittedCode(frame, engine);
}
#endif // V4_ENABLE_JIT
+ // interpreter
if (debugger)
debugger->enteringFunction();
- if (function->jittedCode != nullptr && debugger == nullptr) {
- acc = function->jittedCode(&frame, engine);
- } else {
- // interpreter
- const uchar *code = function->codeData;
- const uchar *codeStart = code;
+ ReturnedValue result = interpret(frame, engine, function->codeData);
+
+ if (debugger)
+ debugger->leavingFunction(result);
+
+ return result;
+}
+
+QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code)
+{
+ QV4::Function *function = frame->v4Function;
+ QV4::Value &accumulator = frame->jsFrame->accumulator;
+ QV4::ReturnedValue acc = accumulator.asReturnedValue();
+ Value *stack = reinterpret_cast<Value *>(frame->jsFrame);
MOTH_JUMP_TABLE;
@@ -629,12 +505,14 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(LoadLocal)
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
acc = cc->locals[index].asReturnedValue();
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
CHECK_EXCEPTION;
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc);
MOTH_END_INSTR(StoreLocal)
@@ -689,58 +567,37 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(LoadElement)
STORE_IP();
- acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
- STORE_IP();
STORE_ACC();
acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadElementA)
+ MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
STORE_IP();
STORE_ACC();
- if (!Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator) && function->isStrict())
- engine->throwTypeError();
+ Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
STORE_IP();
- acc = Runtime::method_loadProperty(engine, STACK_VALUE(base), name);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
- STORE_IP();
STORE_ACC();
acc = Runtime::method_loadProperty(engine, accumulator, name);
CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadPropertyA)
+ MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
- QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
- acc = l->getter(l, engine, STACK_VALUE(base));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(GetLookup)
-
- MOTH_BEGIN_INSTR(GetLookupA)
- STORE_IP();
STORE_ACC();
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(GetLookupA)
+ MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
STORE_IP();
STORE_ACC();
- if (!Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator) && function->isStrict())
- engine->throwTypeError();
+ Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreProperty)
@@ -753,6 +610,20 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(SetLookup)
+ MOTH_BEGIN_INSTR(LoadSuperProperty)
+ STORE_IP();
+ STORE_ACC();
+ acc = Runtime::method_loadSuperProperty(engine, STACK_VALUE(property));
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(LoadSuperProperty)
+
+ MOTH_BEGIN_INSTR(StoreSuperProperty)
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_storeSuperProperty(engine, STACK_VALUE(property), accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(StoreSuperProperty)
+
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
STORE_ACC();
Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator);
@@ -784,14 +655,33 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadIdObject)
+ MOTH_BEGIN_INSTR(Yield)
+ frame->yield = code;
+ return acc;
+ MOTH_END_INSTR(Yield)
+
+ MOTH_BEGIN_INSTR(Resume)
+ // check exception, in case the generator was called with throw() or return()
+ if (engine->hasException) {
+ // an empty value indicates that the generator was called with return()
+ if (engine->exceptionValue->asReturnedValue() != Primitive::emptyValue().asReturnedValue())
+ goto handleUnwind;
+ engine->hasException = false;
+ *engine->exceptionValue = Primitive::undefinedValue();
+ } else {
+ code += offset;
+ }
+ MOTH_END_INSTR(Resume)
+
MOTH_BEGIN_INSTR(CallValue)
STORE_IP();
Value func = STACK_VALUE(name);
if (Q_UNLIKELY(!func.isFunctionObject())) {
acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- goto catchException;
+ goto handleUnwind;
}
- acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc);
+ Value undef = Primitive::undefinedValue();
+ acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallValue)
@@ -809,7 +699,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
if (Q_UNLIKELY(!f.isFunctionObject())) {
acc = engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
@@ -852,15 +742,49 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(CallContextObjectProperty)
- MOTH_BEGIN_INSTR(SetExceptionHandler)
- exceptionHandler = offset ? code + offset : nullptr;
- MOTH_END_INSTR(SetExceptionHandler)
+ MOTH_BEGIN_INSTR(CallWithSpread)
+ STORE_IP();
+ acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallWithSpread)
+
+ MOTH_BEGIN_INSTR(Construct)
+ STORE_IP();
+ acc = Runtime::method_construct(engine, STACK_VALUE(func), ACC, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(ConstructWithSpread)
+ STORE_IP();
+ acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), ACC, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(ConstructWithSpread)
+
+ MOTH_BEGIN_INSTR(SetUnwindHandler)
+ frame->unwindHandler = offset ? code + offset : nullptr;
+ MOTH_END_INSTR(SetUnwindHandler)
+
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ CHECK_EXCEPTION;
+ if (frame->unwindLevel) {
+ --frame->unwindLevel;
+ if (frame->unwindLevel)
+ goto handleUnwind;
+ code = frame->unwindLabel;
+ }
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ frame->unwindLevel = level;
+ frame->unwindLabel = code + offset;
+ goto handleUnwind;
+ MOTH_END_INSTR(UnwindToLabel)
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
Runtime::method_throwException(engine, accumulator);
- goto catchException;
+ goto handleUnwind;
MOTH_END_INSTR(ThrowException)
MOTH_BEGIN_INSTR(GetException)
@@ -870,71 +794,90 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(HasException)
MOTH_BEGIN_INSTR(SetException)
- *engine->exceptionValue = acc;
- engine->hasException = true;
+ if (acc != Primitive::emptyValue().asReturnedValue()) {
+ *engine->exceptionValue = acc;
+ engine->hasException = true;
+ }
MOTH_END_INSTR(SetException)
MOTH_BEGIN_INSTR(PushCatchContext)
- STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, name);
+ STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name);
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(CreateCallContext)
- stack[CallData::Context] = ExecutionContext::newCallContext(&frame);
+ stack[CallData::Context] = ExecutionContext::newCallContext(frame);
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushWithContext)
STORE_IP();
STORE_ACC();
- accumulator = accumulator.toObject(engine);
+ auto ctx = Runtime::method_createWithContext(engine, stack);
CHECK_EXCEPTION;
- STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator);
+ STACK_VALUE(CallData::Context) = ctx;
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ STORE_ACC();
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index);
+ MOTH_END_INSTR(PushBlockContext)
+
+ MOTH_BEGIN_INSTR(CloneBlockContext)
+ STORE_ACC();
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c);
+ MOTH_END_INSTR(CloneBlockContext)
+
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index);
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine);
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
- STACK_VALUE(CallData::Context) = STACK_VALUE(reg);
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = c->d()->outer;
MOTH_END_INSTR(PopContext)
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
+ MOTH_BEGIN_INSTR(GetIterator)
STORE_ACC();
- acc = Runtime::method_foreachIterator(engine, accumulator);
+ acc = Runtime::method_getIterator(engine, accumulator, iterator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(ForeachIteratorObject)
+ MOTH_END_INSTR(GetIterator)
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
+ MOTH_BEGIN_INSTR(IteratorNext)
STORE_ACC();
- acc = Runtime::method_foreachNextPropertyName(accumulator);
+ acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value));
CHECK_EXCEPTION;
- MOTH_END_INSTR(ForeachNextPropertyName)
+ MOTH_END_INSTR(IteratorNext)
- MOTH_BEGIN_INSTR(DeleteMember)
- if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) {
- if (function->isStrict()) {
- STORE_IP();
- engine->throwTypeError();
- goto catchException;
- }
- acc = Encode(false);
- } else {
- acc = Encode(true);
- }
- MOTH_END_INSTR(DeleteMember)
+ MOTH_BEGIN_INSTR(IteratorClose)
+ STORE_ACC();
+ acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done));
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(IteratorClose)
+
+ MOTH_BEGIN_INSTR(DestructureRestElement)
+ STORE_ACC();
+ acc = Runtime::method_destructureRestElement(engine, ACC);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(DestructureRestElement)
- MOTH_BEGIN_INSTR(DeleteSubscript)
- if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) {
+ MOTH_BEGIN_INSTR(DeleteProperty)
+ if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) {
if (function->isStrict()) {
STORE_IP();
engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
acc = Encode(true);
}
- MOTH_END_INSTR(DeleteSubscript)
+ MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
if (!Runtime::method_deleteName(engine, name)) {
@@ -942,7 +885,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STORE_IP();
QString n = function->compilationUnit->runtimeStrings[name]->toQString();
engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n));
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -970,9 +913,13 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(DefineObjectLiteral)
QV4::Value *arguments = stack + args;
- acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags);
+ acc = Runtime::method_objectLiteral(engine, internalClassId, argc, arguments);
MOTH_END_INSTR(DefineObjectLiteral)
+ MOTH_BEGIN_INSTR(CreateClass)
+ acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames);
+ MOTH_END_INSTR(CreateClass)
+
MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
acc = Runtime::method_createMappedArgumentsObject(engine);
MOTH_END_INSTR(CreateMappedArgumentsObject)
@@ -981,6 +928,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Runtime::method_createUnmappedArgumentsObject(engine);
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_BEGIN_INSTR(CreateRestParameter)
+ acc = Runtime::method_createRestParameter(engine, argIndex);
+ MOTH_END_INSTR(CreateRestParameter)
+
MOTH_BEGIN_INSTR(ConvertThisToObject)
Value *t = &stack[CallData::This];
if (!t->isObject()) {
@@ -993,11 +944,20 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(ConvertThisToObject)
- MOTH_BEGIN_INSTR(Construct)
- STORE_IP();
- acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc);
+ MOTH_BEGIN_INSTR(LoadSuperConstructor)
+ const Value *f = &stack[CallData::Function];
+ if (!f->isFunctionObject()) {
+ engine->throwTypeError();
+ } else {
+ acc = static_cast<const Object *>(f)->getPrototypeOf()->asReturnedValue();
+ }
CHECK_EXCEPTION;
- MOTH_END_INSTR(Construct)
+ MOTH_END_INSTR(LoadSuperConstructor)
+
+ MOTH_BEGIN_INSTR(ToObject)
+ acc = ACC.toObject(engine)->asReturnedValue();
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
code += offset;
@@ -1023,6 +983,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNoException)
+ if (!engine->hasException)
+ code += offset;
+ MOTH_END_INSTR(JumpNoException)
+
+ MOTH_BEGIN_INSTR(JumpNotUndefined)
+ if (Q_LIKELY(acc != QV4::Encode::undefined()))
+ code += offset;
+ MOTH_END_INSTR(JumpNotUndefined)
+
MOTH_BEGIN_INSTR(CmpEqNull)
acc = Encode(ACC.isNullOrUndefined());
MOTH_END_INSTR(CmpEqNull)
@@ -1059,7 +1029,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Encode(left.int_32() == ACC.int_32());
} else {
STORE_ACC();
- acc = Encode(compareEqual(left, accumulator));
+ acc = Encode(bool(Runtime::method_compareEqual(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpEq)
@@ -1070,7 +1040,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Encode(bool(left.int_32() != ACC.int_32()));
} else {
STORE_ACC();
- acc = Encode(!compareEqual(left, accumulator));
+ acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpNe)
@@ -1154,27 +1124,11 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(CmpIn)
MOTH_BEGIN_INSTR(CmpInstanceOf)
- // 11.8.6, 5: rval must be an Object
- if (Q_UNLIKELY(!Primitive::fromReturnedValue(acc).isObject())) {
- acc = engine->throwTypeError();
- goto catchException;
- }
-
- // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result.
- acc = Primitive::fromReturnedValue(acc).objectValue()->instanceOf(STACK_VALUE(lhs));
+ STORE_ACC();
+ acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC);
CHECK_EXCEPTION;
MOTH_END_INSTR(CmpInstanceOf)
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
if (ACC.integerCompatible()) {
acc = Encode(!static_cast<bool>(ACC.int_32()));
@@ -1259,6 +1213,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(Sub)
+ MOTH_BEGIN_INSTR(Exp)
+ const Value left = STACK_VALUE(lhs);
+ double base = left.toNumber();
+ double exp = ACC.toNumber();
+ if (qIsInf(exp) && (base == 1 || base == -1))
+ acc = Encode(qSNaN());
+ else
+ acc = Encode(pow(base,exp));
+ MOTH_END_INSTR(Exp)
+
MOTH_BEGIN_INSTR(Mul)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
@@ -1351,7 +1315,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(ShlConst)
MOTH_BEGIN_INSTR(Ret)
- goto functionExit;
+ return acc;
MOTH_END_INSTR(Ret)
MOTH_BEGIN_INSTR(Debug)
@@ -1369,21 +1333,12 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlImportedScripts)
- catchException:
- Q_ASSERT(engine->hasException);
- if (!exceptionHandler) {
+ handleUnwind:
+ Q_ASSERT(engine->hasException || frame->unwindLevel);
+ if (!frame->unwindHandler) {
acc = Encode::undefined();
- goto functionExit;
+ return acc;
}
- code = exceptionHandler;
- }
+ code = frame->unwindHandler;
}
-
-functionExit:
- if (QV4::Debugging::Debugger *debugger = engine->debugger())
- debugger->leavingFunction(ACC.asReturnedValue());
- engine->currentStackFrame = frame.parent;
- engine->jsStackTop = stack;
-
- return acc;
}
diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h
index 3b7723ca7e..8a76e60f20 100644
--- a/src/qml/jsruntime/qv4vme_moth_p.h
+++ b/src/qml/jsruntime/qv4vme_moth_p.h
@@ -65,13 +65,8 @@ public:
QV4::Function *function;
const QV4::ExecutionContext *scope;
};
- static inline
- QV4::ReturnedValue exec(Function *v4Function, const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
- ExecData data{v4Function, context};
- quintptr d = reinterpret_cast<quintptr>(&data) | 0x1;
- return exec(reinterpret_cast<const FunctionObject *>(d), thisObject, argv, argc);
- }
- static QV4::ReturnedValue exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
+ static QV4::ReturnedValue exec(CppStackFrame *frame, ExecutionEngine *engine);
+ static QV4::ReturnedValue interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *codeEntry);
};
} // namespace Moth
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
new file mode 100644
index 0000000000..b4e868d45d
--- /dev/null
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4VTABLE_P_H
+#define QV4VTABLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4global_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct VTable
+{
+ typedef void (*Destroy)(Heap::Base *);
+ typedef void (*MarkObjects)(Heap::Base *, MarkStack *markStack);
+ typedef bool (*IsEqualTo)(Managed *m, Managed *other);
+
+ typedef ReturnedValue (*Get)(const Managed *, PropertyKey id, const Value *receiver, bool *hasProperty);
+ typedef bool (*Put)(Managed *, PropertyKey id, const Value &value, Value *receiver);
+ typedef bool (*DeleteProperty)(Managed *m, PropertyKey id);
+ typedef bool (*HasProperty)(const Managed *m, PropertyKey id);
+ typedef PropertyAttributes (*GetOwnProperty)(Managed *m, PropertyKey id, Property *p);
+ typedef bool (*DefineOwnProperty)(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+ typedef bool (*IsExtensible)(const Managed *);
+ typedef bool (*PreventExtensions)(Managed *);
+ typedef Heap::Object *(*GetPrototypeOf)(const Managed *);
+ typedef bool (*SetPrototypeOf)(Managed *, const Object *);
+ typedef qint64 (*GetLength)(const Managed *m);
+ typedef void (*AdvanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ typedef ReturnedValue (*InstanceOf)(const Object *typeObject, const Value &var);
+
+ 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);
+
+ const VTable * const parent;
+ quint32 inlinePropertyOffset : 16;
+ quint32 nInlineProperties : 16;
+ quint8 isExecutionContext;
+ quint8 isString;
+ quint8 isObject;
+ quint8 isFunctionObject;
+ quint8 isErrorObject;
+ quint8 isArrayData;
+ quint8 isStringOrSymbol;
+ quint8 type;
+ quint8 unused[4];
+ const char *className;
+
+ Destroy destroy;
+ MarkObjects markObjects;
+ IsEqualTo isEqualTo;
+
+ Get get;
+ Put put;
+ DeleteProperty deleteProperty;
+ HasProperty hasProperty;
+ GetOwnProperty getOwnProperty;
+ DefineOwnProperty defineOwnProperty;
+ IsExtensible isExtensible;
+ PreventExtensions preventExtensions;
+ GetPrototypeOf getPrototypeOf;
+ SetPrototypeOf setPrototypeOf;
+ GetLength getLength;
+ AdvanceIterator advanceIterator;
+ InstanceOf instanceOf;
+
+ Call call;
+ CallAsConstructor callAsConstructor;
+};
+
+
+struct VTableBase {
+protected:
+ static constexpr VTable::Destroy virtualDestroy = nullptr;
+ static constexpr VTable::IsEqualTo virtualIsEqualTo = nullptr;
+
+ static constexpr VTable::Get virtualGet = nullptr;
+ static constexpr VTable::Put virtualPut = nullptr;
+ static constexpr VTable::DeleteProperty virtualDeleteProperty = nullptr;
+ static constexpr VTable::HasProperty virtualHasProperty = nullptr;
+ static constexpr VTable::GetOwnProperty virtualGetOwnProperty = nullptr;
+ static constexpr VTable::DefineOwnProperty virtualDefineOwnProperty = nullptr;
+ static constexpr VTable::IsExtensible virtualIsExtensible = nullptr;
+ static constexpr VTable::PreventExtensions virtualPreventExtensions = nullptr;
+ static constexpr VTable::GetPrototypeOf virtualGetPrototypeOf = nullptr;
+ static constexpr VTable::SetPrototypeOf virtualSetPrototypeOf = nullptr;
+ static constexpr VTable::GetLength virtualGetLength = nullptr;
+ static constexpr VTable::AdvanceIterator virtualAdvanceIterator = nullptr;
+ static constexpr VTable::InstanceOf virtualInstanceOf = nullptr;
+
+ static constexpr VTable::Call virtualCall = nullptr;
+ static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr;
+};
+
+#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
+{ \
+ parentVTable, \
+ (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
+ (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \
+ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
+ classname::IsExecutionContext, \
+ classname::IsString, \
+ classname::IsObject, \
+ classname::IsFunctionObject, \
+ classname::IsErrorObject, \
+ classname::IsArrayData, \
+ classname::IsStringOrSymbol, \
+ classname::MyType, \
+ { 0, 0, 0, 0 }, \
+ #classname, \
+ \
+ classname::virtualDestroy, \
+ classname::Data::markObjects, \
+ classname::virtualIsEqualTo, \
+ \
+ classname::virtualGet, \
+ classname::virtualPut, \
+ classname::virtualDeleteProperty, \
+ classname::virtualHasProperty, \
+ classname::virtualGetOwnProperty, \
+ classname::virtualDefineOwnProperty, \
+ classname::virtualIsExtensible, \
+ classname::virtualPreventExtensions, \
+ classname::virtualGetPrototypeOf, \
+ classname::virtualSetPrototypeOf, \
+ classname::virtualGetLength, \
+ classname::virtualAdvanceIterator, \
+ classname::virtualInstanceOf, \
+ \
+ classname::virtualCall, \
+ classname::virtualCallAsConstructor, \
+}
+
+#define DEFINE_MANAGED_VTABLE(classname) \
+const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0)
+
+#define V4_OBJECT2(DataClass, superClass) \
+ private: \
+ DataClass() Q_DECL_EQ_DELETE; \
+ Q_DISABLE_COPY(DataClass) \
+ public: \
+ Q_MANAGED_CHECK \
+ typedef QV4::Heap::DataClass Data; \
+ typedef superClass SuperClass; \
+ static const QV4::VTable static_vtbl; \
+ static inline const QV4::VTable *staticVTable() { return &static_vtbl; } \
+ V4_MANAGED_SIZE_TEST \
+ QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \
+ QV4::Heap::DataClass *d() const { \
+ QV4::Heap::DataClass *dptr = d_unchecked(); \
+ dptr->_checkIsInitialized(); \
+ return dptr; \
+ } \
+ Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
+
+#define V4_PROTOTYPE(p) \
+ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \
+ { return e->p(); }
+
+
+#define DEFINE_OBJECT_VTABLE_BASE(classname) \
+ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl)
+
+#define DEFINE_OBJECT_VTABLE(classname) \
+DEFINE_OBJECT_VTABLE_BASE(classname)
+
+#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \
+template<> DEFINE_OBJECT_VTABLE_BASE(classname)
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
index 53933cd090..07af5a6f7a 100644
--- a/src/qml/memory/qv4heap_p.h
+++ b/src/qml/memory/qv4heap_p.h
@@ -54,7 +54,7 @@
#include <private/qv4global_p.h>
#include <private/qv4mmdefs_p.h>
#include <private/qv4writebarrier_p.h>
-#include <private/qv4internalclass_p.h>
+#include <private/qv4vtable_p.h>
#include <QSharedPointer>
// To check if Heap::Base::init is called (meaning, all subclasses did their init and called their
@@ -65,40 +65,43 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-struct InternalClass;
+namespace Heap {
-struct VTable
-{
- const VTable * const parent;
- uint inlinePropertyOffset : 16;
- uint nInlineProperties : 16;
- uint isExecutionContext : 1;
- uint isString : 1;
- uint isObject : 1;
- uint isFunctionObject : 1;
- uint isErrorObject : 1;
- uint isArrayData : 1;
- uint unused : 18;
- uint type : 8;
- const char *className;
- void (*destroy)(Heap::Base *);
- void (*markObjects)(Heap::Base *, MarkStack *markStack);
- bool (*isEqualTo)(Managed *m, Managed *other);
-};
+template <typename T, size_t o>
+struct Pointer {
+ static Q_CONSTEXPR size_t offset = o;
+ T operator->() const { return get(); }
+ operator T () const { return get(); }
-namespace Heap {
+ Base *base();
+
+ void set(EngineBase *e, T newVal) {
+ WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Base *>(newVal));
+ }
+
+ T get() const { return reinterpret_cast<T>(ptr); }
+
+ template <typename Type>
+ Type *cast() { return static_cast<Type *>(ptr); }
+
+ Base *heapObject() const { return ptr; }
+
+private:
+ Base *ptr;
+};
+typedef Pointer<char *, 0> V4PointerCheck;
+Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value);
struct Q_QML_EXPORT Base {
void *operator new(size_t) = delete;
- static void markObjects(Heap::Base *, MarkStack *) {}
+ static void markObjects(Base *, MarkStack *);
- InternalClass *internalClass;
+ Pointer<InternalClass *, 0> internalClass;
inline ReturnedValue asReturnedValue() const;
inline void mark(QV4::MarkStack *markStack);
- const VTable *vtable() const { return internalClass->vtable; }
inline bool isMarked() const {
const HeapItem *h = reinterpret_cast<const HeapItem *>(this);
Chunk *c = h->chunk();
@@ -125,13 +128,9 @@ struct Q_QML_EXPORT Base {
return Chunk::testBit(c->objectBitmap, h - c->realBase());
}
- inline void markChildren(MarkStack *markStack) {
- vtable()->markObjects(this, markStack);
- }
-
void *operator new(size_t, Managed *m) { return m; }
- void *operator new(size_t, Heap::Base *m) { return m; }
- void operator delete(void *, Heap::Base *) {}
+ void *operator new(size_t, Base *m) { return m; }
+ void operator delete(void *, Base *) {}
void init() { _setInitialized(); }
void destroy() { _setDestroyed(); }
@@ -178,10 +177,8 @@ Q_STATIC_ASSERT(std::is_standard_layout<Base>::value);
Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0);
Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE);
-}
-
inline
-void Heap::Base::mark(QV4::MarkStack *markStack)
+void Base::mark(QV4::MarkStack *markStack)
{
Q_ASSERT(inUse());
const HeapItem *h = reinterpret_cast<const HeapItem *>(this);
@@ -196,36 +193,12 @@ void Heap::Base::mark(QV4::MarkStack *markStack)
}
}
-namespace Heap {
-
-template <typename T, size_t o>
-struct Pointer {
- static Q_CONSTEXPR size_t offset = o;
- T operator->() const { return get(); }
- operator T () const { return get(); }
-
- Heap::Base *base() {
- Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base));
- Q_ASSERT(base->inUse());
- return base;
- }
-
- void set(EngineBase *e, T newVal) {
- WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Heap::Base *>(newVal));
- }
-
- T get() const { return reinterpret_cast<T>(ptr); }
-
- template <typename Type>
- Type *cast() { return static_cast<Type *>(ptr); }
-
- Heap::Base *heapObject() const { return ptr; }
-
-private:
- Heap::Base *ptr;
-};
-typedef Pointer<char *, 0> V4PointerCheck;
-Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value);
+template<typename T, size_t o>
+Base *Pointer<T, o>::base() {
+ Base *base = reinterpret_cast<Base *>(this) - (offset/sizeof(Base *));
+ Q_ASSERT(base->inUse());
+ return base;
+}
}
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 4e83abebdf..d9772e608e 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -42,6 +42,7 @@
#include "qv4objectproto_p.h"
#include "qv4mm_p.h"
#include "qv4qobjectwrapper_p.h"
+#include "qv4identifiertable_p.h"
#include <QtCore/qalgorithms.h>
#include <QtCore/private/qnumeric_p.h>
#include <QtCore/qloggingcategory.h>
@@ -335,7 +336,7 @@ bool Chunk::sweep(ExecutionEngine *engine)
HeapItem *itemToFree = o + index;
Heap::Base *b = *itemToFree;
- const VTable *v = b->vtable();
+ const VTable *v = b->internalClass->vtable;
// if (Q_UNLIKELY(classCountPtr))
// classCountPtr(v->className);
if (v->destroy) {
@@ -389,8 +390,8 @@ void Chunk::freeAll(ExecutionEngine *engine)
HeapItem *itemToFree = o + index;
Heap::Base *b = *itemToFree;
- if (b->vtable()->destroy) {
- b->vtable()->destroy(b);
+ if (b->internalClass->vtable->destroy) {
+ b->internalClass->vtable->destroy(b);
b->_checkIsDestroyed();
}
#ifdef V4_USE_HEAPTRACK
@@ -412,10 +413,6 @@ void Chunk::resetBlackBits()
memset(blackBitmap, 0, sizeof(blackBitmap));
}
-#ifdef MM_STATS
-static uint nGrayItems = 0;
-#endif
-
void Chunk::collectGrayItems(MarkStack *markStack)
{
// DEBUG << "sweeping chunk" << this << (*freeList);
@@ -438,10 +435,6 @@ void Chunk::collectGrayItems(MarkStack *markStack)
Heap::Base *b = *itemToFree;
Q_ASSERT(b->inUse());
markStack->push(b);
-#ifdef MM_STATS
- ++nGrayItems;
-// qDebug() << "adding gray item" << b << "to mark stack";
-#endif
}
grayBitmap[i] = 0;
o += Chunk::Bits;
@@ -459,7 +452,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
#else
const int start = 1;
#endif
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
uint freeSlots = 0;
uint allocatedSlots = 0;
#endif
@@ -469,7 +462,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
if (!i)
usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1;
#endif
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
allocatedSlots += qPopulationCount(usedSlots);
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
#endif
@@ -486,7 +479,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
break;
}
usedSlots = (objectBitmap[i]|extendsBitmap[i]);
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
allocatedSlots += qPopulationCount(usedSlots);
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
#endif
@@ -497,7 +490,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
usedSlots |= (quintptr(1) << index) - 1;
uint freeEnd = i*Bits + index;
uint nSlots = freeEnd - freeStart;
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
// qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots;
freeSlots += nSlots;
#endif
@@ -508,7 +501,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
bins[bin] = freeItem;
}
}
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
Q_ASSERT(freeSlots + allocatedSlots == (EntriesInBitmap - start) * 8 * sizeof(quintptr));
#endif
}
@@ -643,8 +636,9 @@ void BlockAllocator::sweep()
void BlockAllocator::freeAll()
{
- for (auto c : chunks) {
+ for (auto c : chunks)
c->freeAll(engine);
+ for (auto c : chunks) {
Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage);
chunkAllocator->free(c);
}
@@ -691,7 +685,7 @@ static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocato
{
HeapItem *itemToFree = c.chunk->first();
Heap::Base *b = *itemToFree;
- const VTable *v = b->vtable();
+ const VTable *v = b->internalClass->vtable;
if (Q_UNLIKELY(classCountPtr))
classCountPtr(v->className);
@@ -758,6 +752,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
: engine(engine)
, chunkAllocator(new ChunkAllocator)
, blockAllocator(chunkAllocator, engine)
+ , icAllocator(chunkAllocator, engine)
, hugeItemAllocator(chunkAllocator, engine)
, m_persistentValues(new PersistentValueStorage(engine))
, m_weakValues(new PersistentValueStorage(engine))
@@ -774,11 +769,6 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
blockAllocator.allocationStats = statistics.allocations;
}
-#ifdef MM_STATS
-static int allocationCount = 0;
-static size_t lastAllocRequestedSlots = 0;
-#endif
-
Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
{
const size_t stringSize = align(sizeof(Heap::String));
@@ -884,7 +874,7 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable
Chunk::clearBit(c->extendsBitmap, index);
}
o->memberData.set(engine, m);
- m->internalClass = engine->internalClasses[EngineBase::Class_MemberData];
+ m->internalClass.set(engine, engine->internalClasses(EngineBase::Class_MemberData));
Q_ASSERT(o->memberData->internalClass);
m->values.alloc = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value));
m->values.size = o->memberData->values.alloc;
@@ -911,7 +901,7 @@ void MarkStack::drain()
Heap::Base *h = pop();
++markStackSize;
Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen.
- h->markChildren(this);
+ h->internalClass->vtable->markObjects(h, this);
}
}
@@ -1017,8 +1007,13 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
}
}
- blockAllocator.sweep();
- hugeItemAllocator.sweep(classCountPtr);
+
+ if (!lastSweep) {
+ engine->identifierTable->sweep();
+ blockAllocator.sweep(/*classCountPtr*/);
+ hugeItemAllocator.sweep(classCountPtr);
+ icAllocator.sweep(/*classCountPtr*/);
+ }
}
bool MemoryManager::shouldRunGC() const
@@ -1096,10 +1091,6 @@ void MemoryManager::runGC()
qDebug(stats) << "Fragmented memory before GC" << (totalMem - usedBefore);
dumpBins(&blockAllocator);
-#ifdef MM_STATS
- nGrayItems = 0;
-#endif
-
QElapsedTimer t;
t.start();
mark();
@@ -1159,7 +1150,8 @@ void MemoryManager::runGC()
if (aggressiveGC) {
// ensure we don't 'loose' any memory
- Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false));
+ Q_ASSERT(blockAllocator.allocatedMem()
+ == blockAllocator.usedMem() + dumpBins(&blockAllocator, false));
}
usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep;
@@ -1167,16 +1159,17 @@ void MemoryManager::runGC()
// reset all black bits
blockAllocator.resetBlackBits();
hugeItemAllocator.resetBlackBits();
+ icAllocator.resetBlackBits();
}
size_t MemoryManager::getUsedMem() const
{
- return blockAllocator.usedMem();
+ return blockAllocator.usedMem() + icAllocator.usedMem();
}
size_t MemoryManager::getAllocatedMem() const
{
- return blockAllocator.allocatedMem() + hugeItemAllocator.usedMem();
+ return blockAllocator.allocatedMem() + icAllocator.allocatedMem() + hugeItemAllocator.usedMem();
}
size_t MemoryManager::getLargeItemsMem() const
@@ -1193,6 +1186,7 @@ MemoryManager::~MemoryManager()
sweep(/*lastSweep*/true);
blockAllocator.freeAll();
hugeItemAllocator.freeAll();
+ icAllocator.freeAll();
delete m_weakValues;
#ifdef V4_USE_VALGRIND
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index 40670bcdc7..e3a011caf9 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -160,224 +160,98 @@ public:
{ return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
template<typename ManagedType>
- inline typename ManagedType::Data *allocManaged(std::size_t size)
+ inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic)
{
Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value);
size = align(size);
- Heap::Base *o = allocData(size);
- InternalClass *ic = ManagedType::defaultInternalClass(engine);
- ic = ic->changeVTable(ManagedType::staticVTable());
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- return static_cast<typename ManagedType::Data *>(o);
+ typename ManagedType::Data *d = static_cast<typename ManagedType::Data *>(allocData(size));
+ d->internalClass.set(engine, ic);
+ Q_ASSERT(d->internalClass && d->internalClass->vtable);
+ Q_ASSERT(ic->vtable == ManagedType::staticVTable());
+ return d;
}
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
{
- Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value);
- size = align(size);
- Heap::Base *o = allocData(size);
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- Q_ASSERT(ic->vtable == ManagedType::staticVTable());
- return static_cast<typename ManagedType::Data *>(o);
+ return allocManaged<ManagedType>(size, ic->d());
+ }
+
+ template<typename ManagedType>
+ inline typename ManagedType::Data *allocManaged(std::size_t size)
+ {
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
+ return allocManaged<ManagedType>(size, ic);
}
template <typename ObjectType>
- typename ObjectType::Data *allocateObject(InternalClass *ic)
+ typename ObjectType::Data *allocateObject(Heap::InternalClass *ic)
{
Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- Q_ASSERT(ic->vtable == ObjectType::staticVTable());
+ o->internalClass.set(engine, ic);
+ Q_ASSERT(o->internalClass.get() && o->vtable());
+ Q_ASSERT(o->vtable() == ObjectType::staticVTable());
return static_cast<typename ObjectType::Data *>(o);
}
template <typename ObjectType>
+ typename ObjectType::Data *allocateObject(InternalClass *ic)
+ {
+ return allocateObject<ObjectType>(ic->d());
+ }
+
+ template <typename ObjectType>
typename ObjectType::Data *allocateObject()
{
- InternalClass *ic = ObjectType::defaultInternalClass(engine);
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, ObjectType::defaultInternalClass(engine));
ic = ic->changeVTable(ObjectType::staticVTable());
ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d());
- Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- Q_ASSERT(o->internalClass->prototype == ObjectType::defaultPrototype(engine)->d());
- return static_cast<typename ObjectType::Data *>(o);
+ return allocateObject<ObjectType>(ic);
}
template <typename ManagedType, typename Arg1>
typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1)
{
typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize));
- o->internalClass = ManagedType::defaultInternalClass(engine);
+ o->internalClass.set(engine, ManagedType::defaultInternalClass(engine));
Q_ASSERT(o->internalClass && o->internalClass->vtable);
o->init(arg1);
return o;
}
- template <typename ObjectType>
- typename ObjectType::Data *allocObject(InternalClass *ic)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d_unchecked()->init();
- return t->d();
- }
-
- template <typename ObjectType>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init();
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1, arg2);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1, arg2, arg3);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1, arg2, arg3, arg4);
- return t->d();
- }
-
- template <typename ObjectType>
- typename ObjectType::Data *allocObject()
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init();
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1>
- typename ObjectType::Data *allocObject(Arg1 arg1)
+ template <typename ObjectType, typename... Args>
+ typename ObjectType::Data *allocObject(Heap::InternalClass *ic, Args... args)
{
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2>
- typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1, arg2);
- return t->d();
+ typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
+ d->init(args...);
+ return d;
}
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3>
- typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3)
+ template <typename ObjectType, typename... Args>
+ typename ObjectType::Data *allocObject(InternalClass *ic, Args... args)
{
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1, arg2, arg3);
- return t->d();
+ typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
+ d->init(args...);
+ return d;
}
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
+ template <typename ObjectType, typename... Args>
+ typename ObjectType::Data *allocate(Args... args)
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1, arg2, arg3, arg4);
- return t->d();
- }
-
-
- template <typename ManagedType>
- typename ManagedType::Data *alloc()
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init();
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1>
- typename ManagedType::Data *alloc(Arg1 arg1)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1);
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1, typename Arg2>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2);
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2, arg3);
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2, arg3, arg4);
+ t->d_unchecked()->init(args...);
return t->d();
}
- template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
+ template <typename ManagedType, typename... Args>
+ typename ManagedType::Data *alloc(Args... args)
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2, arg3, arg4, arg5);
+ t->d_unchecked()->init(args...);
return t->d();
}
@@ -392,6 +266,14 @@ public:
// called when a JS object grows itself. Specifically: Heap::String::append
void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; }
+ template<typename ManagedType>
+ typename ManagedType::Data *allocIC()
+ {
+ size_t size = align(sizeof(typename ManagedType::Data));
+ Heap::Base *b = *icAllocator.allocate(size, true);
+ return static_cast<typename ManagedType::Data *>(b);
+ }
+
protected:
/// expects size to be aligned
Heap::Base *allocString(std::size_t unmanagedSize);
@@ -409,6 +291,7 @@ public:
QV4::ExecutionEngine *engine;
ChunkAllocator *chunkAllocator;
BlockAllocator blockAllocator;
+ BlockAllocator icAllocator;
HugeItemAllocator hugeItemAllocator;
PersistentValueStorage *m_persistentValues;
PersistentValueStorage *m_weakValues;
@@ -423,6 +306,9 @@ public:
bool gcStats = false;
bool gcCollectorStats = false;
+ int allocationCount = 0;
+ size_t lastAllocRequestedSlots = 0;
+
struct {
size_t maxReservedMem = 0;
size_t maxAllocatedMem = 0;
diff --git a/src/qml/parser/parser.pri b/src/qml/parser/parser.pri
index e5b8ae2749..adab4ef9a2 100644
--- a/src/qml/parser/parser.pri
+++ b/src/qml/parser/parser.pri
@@ -3,20 +3,24 @@ HEADERS += \
$$PWD/qqmljsastfwd_p.h \
$$PWD/qqmljsastvisitor_p.h \
$$PWD/qqmljsengine_p.h \
- $$PWD/qqmljsgrammar_p.h \
$$PWD/qqmljslexer_p.h \
$$PWD/qqmljsmemorypool_p.h \
- $$PWD/qqmljsparser_p.h \
$$PWD/qqmljsglobal_p.h \
$$PWD/qqmljskeywords_p.h \
+ $$PWD/qqmljsengine_p.h \
+ $$PWD/qqmljsglobal_p.h
SOURCES += \
$$PWD/qqmljsast.cpp \
$$PWD/qqmljsastvisitor.cpp \
$$PWD/qqmljsengine_p.cpp \
- $$PWD/qqmljsgrammar.cpp \
$$PWD/qqmljslexer.cpp \
- $$PWD/qqmljsparser.cpp \
-OTHER_FILES += \
- $$PWD/qqmljs.g
+CONFIG += qlalr
+QLALRSOURCES = $$PWD/qqmljs.g
+QMAKE_QLALRFLAGS = --no-debug --qt
+
+OTHER_FILES += $$QLALRSOURCES
+
+# make sure we install the headers generated by qlalr
+private_headers.CONFIG += no_check_exist
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index d2d947e55c..cd40a77299 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -40,8 +40,7 @@
%parser QQmlJSGrammar
%decl qqmljsparser_p.h
%impl qqmljsparser.cpp
-%expect 5
-%expect-rr 2
+%expect 1
%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&="
%token T_BREAK "break" T_CASE "case" T_CATCH "catch"
@@ -64,7 +63,8 @@
%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%"
%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")"
%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*"
-%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal"
+%token T_STAR_STAR "**" T_STAR_STAR_EQ "**=" T_STAR_EQ "*="
+%token T_STRING_LITERAL "string literal"
%token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly"
%token T_SWITCH "switch" T_THIS "this" T_THROW "throw"
%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof"
@@ -77,14 +77,29 @@
%token T_MULTILINE_STRING_LITERAL "multiline string literal"
%token T_COMMENT "comment"
%token T_COMPATIBILITY_SEMICOLON
+%token T_ARROW "=>"
%token T_ENUM "enum"
+%token T_ELLIPSIS "..."
+%token T_YIELD "yield"
+%token T_SUPER "super"
+%token T_CLASS "class"
+%token T_EXTENDS "extends"
+%token T_STATIC "static"
+%token T_EXPORT "export"
+%token T_FROM "from"
+
+--- template strings
+%token T_NO_SUBSTITUTION_TEMPLATE"(no subst template)"
+%token T_TEMPLATE_HEAD "(template head)"
+%token T_TEMPLATE_MIDDLE "(template middle)"
+%token T_TEMPLATE_TAIL "(template tail)"
--- context keywords.
%token T_PUBLIC "public"
%token T_IMPORT "import"
%token T_PRAGMA "pragma"
%token T_AS "as"
-%token T_ON "on"
+%token T_OF "of"
%token T_GET "get"
%token T_SET "set"
@@ -95,11 +110,16 @@
%token T_FEED_UI_OBJECT_MEMBER
%token T_FEED_JS_STATEMENT
%token T_FEED_JS_EXPRESSION
-%token T_FEED_JS_SOURCE_ELEMENT
-%token T_FEED_JS_PROGRAM
+%token T_FEED_JS_SCRIPT
+%token T_FEED_JS_MODULE
-%nonassoc SHIFT_THERE
-%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET
+--- Lookahead handling
+%token T_FORCE_DECLARATION "(force decl)"
+%token T_FORCE_BLOCK "(force block)"
+%token T_FOR_LOOKAHEAD_OK "(for lookahead ok)"
+
+--%left T_PLUS T_MINUS
+%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET T_OF T_STATIC T_FROM
%nonassoc REDUCE_HERE
%start TopLevel
@@ -143,10 +163,10 @@
**
****************************************************************************/
-#include "qqmljsengine_p.h"
-#include "qqmljslexer_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsmemorypool_p.h"
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsmemorypool_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qcoreapplication.h>
@@ -221,10 +241,10 @@
#ifndef QQMLJSPARSER_P_H
#define QQMLJSPARSER_P_H
-#include "qqmljsglobal_p.h"
-#include "qqmljsgrammar_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsengine_p.h"
+#include <private/qqmljsglobal_p.h>
+#include <private/qqmljsgrammar_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
@@ -241,30 +261,32 @@ public:
union Value {
int ival;
double dval;
+ AST::VariableScope scope;
+ AST::ForEachType forEachType;
AST::ArgumentList *ArgumentList;
AST::CaseBlock *CaseBlock;
AST::CaseClause *CaseClause;
AST::CaseClauses *CaseClauses;
AST::Catch *Catch;
AST::DefaultClause *DefaultClause;
- AST::ElementList *ElementList;
AST::Elision *Elision;
AST::ExpressionNode *Expression;
+ AST::TemplateLiteral *Template;
AST::Finally *Finally;
AST::FormalParameterList *FormalParameterList;
- AST::FunctionBody *FunctionBody;
AST::FunctionDeclaration *FunctionDeclaration;
AST::Node *Node;
AST::PropertyName *PropertyName;
- AST::PropertyAssignment *PropertyAssignment;
- AST::PropertyAssignmentList *PropertyAssignmentList;
- AST::SourceElement *SourceElement;
- AST::SourceElements *SourceElements;
AST::Statement *Statement;
AST::StatementList *StatementList;
AST::Block *Block;
- AST::VariableDeclaration *VariableDeclaration;
AST::VariableDeclarationList *VariableDeclarationList;
+ AST::Pattern *Pattern;
+ AST::PatternElement *PatternElement;
+ AST::PatternElementList *PatternElementList;
+ AST::PatternProperty *PatternProperty;
+ AST::PatternPropertyList *PatternPropertyList;
+ AST::ClassElementList *ClassElementList;
AST::UiProgram *UiProgram;
AST::UiHeaderItemList *UiHeaderItemList;
@@ -290,12 +312,13 @@ public:
~Parser();
// parse a UI program
- bool parse() { return parse(T_FEED_UI_PROGRAM); }
+ bool parse() { ++functionNestingLevel; bool r = parse(T_FEED_UI_PROGRAM); --functionNestingLevel; return r; }
bool parseStatement() { return parse(T_FEED_JS_STATEMENT); }
bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); }
- bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); }
- bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); }
- bool parseProgram() { return parse(T_FEED_JS_PROGRAM); }
+ bool parseUiObjectMember() { ++functionNestingLevel; bool r = parse(T_FEED_UI_OBJECT_MEMBER); --functionNestingLevel; return r; }
+ bool parseProgram() { return parse(T_FEED_JS_SCRIPT); }
+ bool parseScript() { return parse(T_FEED_JS_SCRIPT); }
+ bool parseModule() { return parse(T_FEED_JS_MODULE); }
AST::UiProgram *ast() const
{ return AST::cast<AST::UiProgram *>(program); }
@@ -366,20 +389,30 @@ protected:
AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr);
+ void pushToken(int token);
+ int lookaheadToken(Lexer *lexer);
+
+ void syntaxError(const AST::SourceLocation &location, const char *message) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, QLatin1String(message)));
+ }
+ void syntaxError(const AST::SourceLocation &location, const QString &message) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, message));
+ }
+
protected:
Engine *driver;
MemoryPool *pool;
- int tos;
- int stack_size;
- Value *sym_stack;
- int *state_stack;
- AST::SourceLocation *location_stack;
- QStringRef *string_stack;
+ int tos = 0;
+ int stack_size = 0;
+ Value *sym_stack = nullptr;
+ int *state_stack = nullptr;
+ AST::SourceLocation *location_stack = nullptr;
+ QVector<QStringRef> string_stack;
- AST::Node *program;
+ AST::Node *program = nullptr;
- // error recovery
- enum { TOKEN_BUFFER_SIZE = 3 };
+ // error recovery and lookahead handling
+ enum { TOKEN_BUFFER_SIZE = 5 };
struct SavedToken {
int token;
@@ -388,14 +421,25 @@ protected:
QStringRef spell;
};
- double yylval;
+ int yytoken = -1;
+ double yylval = 0.;
QStringRef yytokenspell;
AST::SourceLocation yylloc;
AST::SourceLocation yyprevlloc;
SavedToken token_buffer[TOKEN_BUFFER_SIZE];
- SavedToken *first_token;
- SavedToken *last_token;
+ SavedToken *first_token = nullptr;
+ SavedToken *last_token = nullptr;
+
+ int functionNestingLevel = 0;
+
+ enum CoverExpressionType {
+ CE_Invalid,
+ CE_ParenthesizedExpression,
+ CE_FormalParameterList
+ };
+ AST::SourceLocation coverExpressionErrorLocation;
+ CoverExpressionType coverExpressionType = CE_Invalid;
QList<DiagnosticMessage> diagnostic_messages;
};
@@ -424,6 +468,8 @@ protected:
// qlalr --no-debug --no-lines --qt qqmljs.g
//
+#define UNIMPLEMENTED syntaxError(loc(1), "Unimplemented"); return false
+
using namespace QQmlJS;
QT_QML_BEGIN_NAMESPACE
@@ -438,22 +484,12 @@ void Parser::reallocateStack()
sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation)));
- string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef)));
+ string_stack.resize(stack_size);
}
Parser::Parser(Engine *engine):
driver(engine),
- pool(engine->pool()),
- tos(0),
- stack_size(0),
- sym_stack(0),
- state_stack(0),
- location_stack(0),
- string_stack(0),
- program(0),
- yylval(0),
- first_token(0),
- last_token(0)
+ pool(engine->pool())
{
}
@@ -463,7 +499,6 @@ Parser::~Parser()
free(sym_stack);
free(state_stack);
free(location_stack);
- free(string_stack);
}
}
@@ -517,17 +552,39 @@ AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode
return 0;
}
+void Parser::pushToken(int token)
+{
+ last_token->token = yytoken;
+ last_token->dval = yylval;
+ last_token->spell = yytokenspell;
+ last_token->loc = yylloc;
+ ++last_token;
+ yytoken = token;
+}
+
+int Parser::lookaheadToken(Lexer *lexer)
+{
+ if (yytoken < 0) {
+ yytoken = lexer->lex();
+ yylval = lexer->tokenValue();
+ yytokenspell = lexer->tokenSpell();
+ yylloc = location(lexer);
+ }
+ return yytoken;
+}
+
+//#define PARSER_DEBUG
bool Parser::parse(int startToken)
{
Lexer *lexer = driver->lexer();
bool hadErrors = false;
- int yytoken = -1;
+ yytoken = -1;
int action = 0;
token_buffer[0].token = startToken;
first_token = &token_buffer[0];
- if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) {
+ if (startToken == T_FEED_JS_SCRIPT && !lexer->qmlMode()) {
Directives ignoreDirectives;
Directives *directives = driver->directives();
if (!directives)
@@ -570,10 +627,19 @@ bool Parser::parse(int startToken)
yytokenspell = first_token->spell;
yylloc = first_token->loc;
++first_token;
+ if (first_token == last_token)
+ first_token = last_token = &token_buffer[0];
}
}
+#ifdef PARSER_DEBUG
+ qDebug() << " in state" << action;
+#endif
+
action = t_action(action, yytoken);
+#ifdef PARSER_DEBUG
+ qDebug() << " current token" << yytoken << (yytoken >= 0 ? spell[yytoken] : "(null)") << "new state" << action;
+#endif
if (action > 0) {
if (action != ACCEPT_STATE) {
yytoken = -1;
@@ -588,6 +654,10 @@ bool Parser::parse(int startToken)
const int r = -action - 1;
tos -= rhs[r];
+#ifdef PARSER_DEBUG
+ qDebug() << " reducing through rule " << -action;
+#endif
+
switch (r) {
./
@@ -595,2582 +665,3463 @@ bool Parser::parse(int startToken)
-- Declarative UI
--------------------------------------------------------------------------------------------------------
-TopLevel: T_FEED_UI_PROGRAM UiProgram ;
+TopLevel: T_FEED_UI_PROGRAM UiProgram;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_STATEMENT Statement ;
+TopLevel: T_FEED_JS_STATEMENT Statement;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_EXPRESSION Expression ;
+TopLevel: T_FEED_JS_EXPRESSION Expression;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_SOURCE_ELEMENT SourceElement ;
+TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ;
+TopLevel: T_FEED_JS_SCRIPT Script;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_PROGRAM Program ;
+TopLevel: T_FEED_JS_MODULE Module;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
+
UiProgram: UiHeaderItemListOpt UiRootMember;
/.
-case $rule_number: {
- sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList,
- sym(2).UiObjectMemberList->finish());
-} break;
+ case $rule_number: {
+ sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList, sym(2).UiObjectMemberList->finish());
+ } break;
./
-UiHeaderItemListOpt: Empty ;
-UiHeaderItemListOpt: UiHeaderItemList ;
+UiHeaderItemListOpt: Empty;
+UiHeaderItemListOpt: UiHeaderItemList;
/.
-case $rule_number: {
- sym(1).Node = sym(1).UiHeaderItemList->finish();
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).UiHeaderItemList->finish();
+ } break;
./
-UiHeaderItemList: UiPragma ;
+UiHeaderItemList: UiPragma;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma);
+ } break;
./
-UiHeaderItemList: UiImport ;
+UiHeaderItemList: UiImport;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport);
+ } break;
./
-UiHeaderItemList: UiHeaderItemList UiPragma ;
+UiHeaderItemList: UiHeaderItemList UiPragma;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma);
+ } break;
./
-UiHeaderItemList: UiHeaderItemList UiImport ;
+UiHeaderItemList: UiHeaderItemList UiImport;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport);
+ } break;
./
-PragmaId: MemberExpression ;
+PragmaId: MemberExpression;
-ImportId: MemberExpression ;
+ImportId: MemberExpression;
-UiPragma: UiPragmaHead T_AUTOMATIC_SEMICOLON ;
-UiPragma: UiPragmaHead T_SEMICOLON ;
+UiPragma: UiPragmaHead T_AUTOMATIC_SEMICOLON;
+UiPragma: UiPragmaHead T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiPragma->semicolonToken = loc(2);
-} break;
+ case $rule_number: {
+ sym(1).UiPragma->semicolonToken = loc(2);
+ } break;
./
-UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_SEMICOLON ;
+UiImport: UiImportHead T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->semicolonToken = loc(2);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->semicolonToken = loc(2);
+ } break;
./
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->semicolonToken = loc(3);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->semicolonToken = loc(3);
+ } break;
./
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->asToken = loc(3);
- sym(1).UiImport->importIdToken = loc(4);
- sym(1).UiImport->importId = stringRef(4);
- sym(1).UiImport->semicolonToken = loc(5);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->asToken = loc(3);
+ sym(1).UiImport->importIdToken = loc(4);
+ sym(1).UiImport->importId = stringRef(4);
+ sym(1).UiImport->semicolonToken = loc(5);
+ } break;
./
-UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ;
+UiImport: UiImportHead T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_AS QmlIdentifier T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->asToken = loc(2);
- sym(1).UiImport->importIdToken = loc(3);
- sym(1).UiImport->importId = stringRef(3);
- sym(1).UiImport->semicolonToken = loc(4);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->asToken = loc(2);
+ sym(1).UiImport->importIdToken = loc(3);
+ sym(1).UiImport->importId = stringRef(3);
+ sym(1).UiImport->semicolonToken = loc(4);
+ } break;
./
-UiPragmaHead: T_PRAGMA PragmaId ;
+UiPragmaHead: T_PRAGMA PragmaId;
/.
-case $rule_number: {
- AST::UiPragma *node = 0;
+ case $rule_number: {
+ AST::UiPragma *node = 0;
- if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) {
- node = new (pool) AST::UiPragma(qualifiedId);
- }
+ if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression))
+ node = new (pool) AST::UiPragma(qualifiedId);
- sym(1).Node = node;
+ sym(1).Node = node;
- if (node) {
- node->pragmaToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
+ if (node) {
+ node->pragmaToken = loc(1);
+ } else {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id")));
- return false; // ### remove me
- }
-} break;
+ return false; // ### remove me
+ }
+ } break;
./
-UiImportHead: T_IMPORT ImportId ;
+UiImportHead: T_IMPORT ImportId;
/.
-case $rule_number: {
- AST::UiImport *node = 0;
-
- if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
- node = new (pool) AST::UiImport(importIdLiteral->value);
- node->fileNameToken = loc(2);
- } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
- node = new (pool) AST::UiImport(qualifiedId);
- node->fileNameToken = loc(2);
- }
+ case $rule_number: {
+ AST::UiImport *node = 0;
- sym(1).Node = node;
+ if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(importIdLiteral->value);
+ node->fileNameToken = loc(2);
+ } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(qualifiedId);
+ node->fileNameToken = loc(2);
+ }
- if (node) {
- node->importToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id or a string literal")));
+ sym(1).Node = node;
- return false; // ### remove me
- }
-} break;
+ if (node) {
+ node->importToken = loc(1);
+ } else {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id or a string literal")));
+
+ return false; // ### remove me
+ }
+ } break;
./
Empty: ;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-UiRootMember: UiObjectDefinition ;
+UiRootMember: UiObjectDefinition;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+ } break;
./
-UiObjectMemberList: UiObjectMember ;
+UiObjectMemberList: UiObjectMember;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+ } break;
./
-UiObjectMemberList: UiObjectMemberList UiObjectMember ;
+UiObjectMemberList: UiObjectMemberList UiObjectMember;
/.
-case $rule_number: {
- AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(
- sym(1).UiObjectMemberList, sym(2).UiObjectMember);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(sym(1).UiObjectMemberList, sym(2).UiObjectMember);
+ sym(1).Node = node;
+ } break;
./
-UiArrayMemberList: UiObjectDefinition ;
+UiArrayMemberList: UiObjectDefinition;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
+ } break;
./
-UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ;
+UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition;
/.
-case $rule_number: {
- AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(
- sym(1).UiArrayMemberList, sym(3).UiObjectMember);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(sym(1).UiArrayMemberList, sym(3).UiObjectMember);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UiObjectInitializer: T_LBRACE T_RBRACE ;
+UiObjectInitializer: T_LBRACE T_RBRACE;
/.
-case $rule_number: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ;
+UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE;
/.
-case $rule_number: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-UiObjectDefinition: UiQualifiedId UiObjectInitializer ;
+UiObjectDefinition: UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId,
- sym(2).UiObjectInitializer);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, sym(2).UiObjectInitializer);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: UiObjectDefinition ;
+UiObjectMember: UiObjectDefinition;
-UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
+UiObjectMember: UiQualifiedId T_COLON ExpressionStatementLookahead T_LBRACKET UiArrayMemberList T_RBRACKET;
/.
-case $rule_number: {
- AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(
- sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish());
- node->colonToken = loc(2);
- node->lbracketToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(sym(1).UiQualifiedId, sym(5).UiArrayMemberList->finish());
+ node->colonToken = loc(2);
+ node->lbracketToken = loc(4);
+ node->rbracketToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
+UiObjectMember: UiQualifiedId T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(1).UiQualifiedId, sym(4).UiQualifiedId, sym(5).UiObjectInitializer);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ;
+UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
- node->colonToken = loc(2);
- node->hasOnToken = true;
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
+ node->colonToken = loc(2);
+ node->hasOnToken = true;
+ sym(1).Node = node;
+ } break;
./
-UiScriptStatement: Block ;
-UiScriptStatement: EmptyStatement ;
-UiScriptStatement: ExpressionStatement ;
-UiScriptStatement: IfStatement ;
-UiScriptStatement: WithStatement ;
-UiScriptStatement: SwitchStatement ;
-UiScriptStatement: TryStatement ;
-UiObjectMember: UiQualifiedId T_COLON UiScriptStatement ;
+UiObjectLiteral: T_LBRACE ExpressionStatementLookahead UiPropertyDefinitionList T_RBRACE;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiObjectLiteral: T_LBRACE ExpressionStatementLookahead UiPropertyDefinitionList T_COMMA T_RBRACE;
/.
-case $rule_number:
-{
- AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(
- sym(1).UiQualifiedId, sym(3).Statement);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ObjectPattern *l = new (pool) AST::ObjectPattern(sym(3).PatternPropertyList->finish());
+ l->lbraceToken = loc(1);
+ l->rbraceToken = loc(4);
+ AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(l);
+ sym(1).Node = node;
+ } break;
./
-UiPropertyType: T_VAR ;
-/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-./
-UiPropertyType: T_RESERVED_WORD ;
+UiScriptStatement: ExpressionStatementLookahead T_FORCE_DECLARATION ExpressionStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead T_FORCE_BLOCK Block;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead T_FORCE_BLOCK UiObjectLiteral;
/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(3).Node;
+ } break;
./
-UiPropertyType: T_IDENTIFIER ;
-/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-./
-UiPropertyType: UiPropertyType T_DOT T_IDENTIFIER ;
+UiScriptStatement: ExpressionStatementLookahead EmptyStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead ExpressionStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead IfStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead WithStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead SwitchStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead TryStatement;
/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3));
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ } break;
./
-UiParameterListOpt: ;
+UiObjectMember: UiQualifiedId T_COLON UiScriptStatement;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+case $rule_number:
+{
+ AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(sym(1).UiQualifiedId, sym(3).Statement);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UiParameterListOpt: UiParameterList ;
+UiPropertyType: T_VAR;
/.
-case $rule_number: {
- sym(1).Node = sym(1).UiParameterList->finish ();
-} break;
+ case $rule_number: {
+ AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiParameterList: UiPropertyType JsIdentifier ;
+UiPropertyType: T_RESERVED_WORD;
/.
-case $rule_number: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2));
- node->propertyTypeToken = loc(1);
- node->identifierToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ;
+UiPropertyType: T_IDENTIFIER;
/.
-case $rule_number: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4));
- node->propertyTypeToken = loc(3);
- node->commaToken = loc(2);
- node->identifierToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ;
+UiPropertyType: UiPropertyType T_DOT T_IDENTIFIER;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->parameters = sym(4).UiParameterList;
- node->semicolonToken = loc(6);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3));
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ;
+UiParameterListOpt: ;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ;
+UiParameterListOpt: UiParameterList;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).UiParameterList->finish();
+ } break;
./
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ;
+UiParameterList: UiPropertyType QmlIdentifier;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4);
- sym(1).Node = node;
-} break;
-./
+ case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2));
+ node->propertyTypeToken = loc(1);
+ node->identifierToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ;
+UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5);
- sym(1).Node = node;
-} break;
-./
-
-UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ;
+ case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4));
+ node->propertyTypeToken = loc(3);
+ node->commaToken = loc(2);
+ node->identifierToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->typeModifier = stringRef(3);
- node->propertyToken = loc(2);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(7);
- node->semicolonToken = loc(8);
- sym(1).Node = node;
-} break;
-./
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->parameters = sym(4).UiParameterList;
+ node->semicolonToken = loc(6);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
-/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3),
- sym(5).Statement);
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->semicolonToken = loc(5);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->typeModifier = stringRef(3);
+ node->propertyToken = loc(2);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(7);
+ node->semicolonToken = loc(8);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), sym(5).Statement);
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement);
+ node->isReadonlyMember = true;
+ node->readonlyToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement);
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
-./
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7); // insert a fake ';' before ':'
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
-/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
- propertyName->identifierToken = loc(6);
- propertyName->next = 0;
-
- AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(
- propertyName, sym(9).UiArrayMemberList->finish());
- binding->colonToken = loc(7);
- binding->lbracketToken = loc(8);
- binding->rbracketToken = loc(10);
-
- node->binding = binding;
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
+ propertyName->identifierToken = loc(6);
+ propertyName->next = 0;
- sym(1).Node = node;
-} break;
+ AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(propertyName, sym(9).UiArrayMemberList->finish());
+ binding->colonToken = loc(7);
+ binding->lbracketToken = loc(8);
+ binding->rbracketToken = loc(10);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ;
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4); // insert a fake ';' before ':'
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4); // insert a fake ';' before ':'
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
- propertyName->identifierToken = loc(3);
- propertyName->next = 0;
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
+ propertyName->identifierToken = loc(3);
+ propertyName->next = 0;
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer);
- binding->colonToken = loc(4);
+ AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
+ propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer);
+ binding->colonToken = loc(4);
- node->binding = binding;
+ node->binding = binding;
- sym(1).Node = node;
-} break;
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ;
+UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5); // insert a fake ';' before ':'
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
+ node->isReadonlyMember = true;
+ node->readonlyToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->semicolonToken = loc(5); // insert a fake ';' before ':'
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4));
- propertyName->identifierToken = loc(4);
- propertyName->next = 0;
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4));
+ propertyName->identifierToken = loc(4);
+ propertyName->next = 0;
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer);
- binding->colonToken = loc(5);
+ AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
+ propertyName, sym(7).UiQualifiedId, sym(8).UiObjectInitializer);
+ binding->colonToken = loc(5);
- node->binding = binding;
+ node->binding = binding;
- sym(1).Node = node;
-} break;
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: FunctionDeclaration ;
+UiObjectMember: FunctionDeclaration;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+ } break;
./
-UiObjectMember: VariableStatement ;
+UiObjectMember: VariableStatement;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+ } break;
+./
+
+UiQualifiedId: MemberExpression;
+/.
+ case $rule_number: {
+ if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
+ QLatin1String("Ignored annotation")));
+
+ sym(1).Expression = mem->base;
+ }
+
+ if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
+ sym(1).UiQualifiedId = qualifiedId;
+ } else {
+ sym(1).UiQualifiedId = 0;
+
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id")));
+
+ return false; // ### recover
+ }
+ } break;
./
UiObjectMember: T_ENUM T_IDENTIFIER T_LBRACE EnumMemberList T_RBRACE;
/.
-case $rule_number: {
- AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish());
- enumDeclaration->enumToken = loc(1);
- enumDeclaration->rbraceToken = loc(5);
- sym(1).Node = enumDeclaration;
- break;
-}
+ case $rule_number: {
+ AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish());
+ enumDeclaration->enumToken = loc(1);
+ enumDeclaration->rbraceToken = loc(5);
+ sym(1).Node = enumDeclaration;
+ break;
+ }
./
EnumMemberList: T_IDENTIFIER;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1));
- node->memberToken = loc(1);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1));
+ node->memberToken = loc(1);
+ sym(1).Node = node;
+ break;
+ }
./
EnumMemberList: T_IDENTIFIER T_EQ T_NUMERIC_LITERAL;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval);
- node->memberToken = loc(1);
- node->valueToken = loc(3);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval);
+ node->memberToken = loc(1);
+ node->valueToken = loc(3);
+ sym(1).Node = node;
+ break;
+ }
./
EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3));
- node->memberToken = loc(3);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3));
+ node->memberToken = loc(3);
+ sym(1).Node = node;
+ break;
+ }
./
EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_NUMERIC_LITERAL;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval);
- node->memberToken = loc(3);
- node->valueToken = loc(5);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval);
+ node->memberToken = loc(3);
+ node->valueToken = loc(5);
+ sym(1).Node = node;
+ break;
+ }
./
-JsIdentifier: T_IDENTIFIER;
+QmlIdentifier: T_IDENTIFIER;
+QmlIdentifier: T_PROPERTY;
+QmlIdentifier: T_SIGNAL;
+QmlIdentifier: T_READONLY;
+QmlIdentifier: T_ON;
+QmlIdentifier: T_GET;
+QmlIdentifier: T_SET;
+QmlIdentifier: T_FROM;
+QmlIdentifier: T_OF;
-JsIdentifier: T_PROPERTY ;
-JsIdentifier: T_SIGNAL ;
-JsIdentifier: T_READONLY ;
-JsIdentifier: T_ON ;
-JsIdentifier: T_GET ;
-JsIdentifier: T_SET ;
+JsIdentifier: T_IDENTIFIER;
+JsIdentifier: T_PROPERTY;
+JsIdentifier: T_SIGNAL;
+JsIdentifier: T_READONLY;
+JsIdentifier: T_ON;
+JsIdentifier: T_GET;
+JsIdentifier: T_SET;
+JsIdentifier: T_FROM;
+JsIdentifier: T_STATIC;
+JsIdentifier: T_OF;
+
+IdentifierReference: JsIdentifier;
+BindingIdentifier: IdentifierReference;
--------------------------------------------------------------------------------------------------------
-- Expressions
--------------------------------------------------------------------------------------------------------
-PrimaryExpression: T_THIS ;
+PrimaryExpression: T_THIS;
/.
-case $rule_number: {
- AST::ThisExpression *node = new (pool) AST::ThisExpression();
- node->thisToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ThisExpression *node = new (pool) AST::ThisExpression();
+ node->thisToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: JsIdentifier ;
+PrimaryExpression: IdentifierReference;
/.
-case $rule_number: {
- AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_NULL ;
+PrimaryExpression: Literal;
+PrimaryExpression: ArrayLiteral;
+PrimaryExpression: ObjectLiteral;
+PrimaryExpression: FunctionExpression;
+PrimaryExpression: ClassExpression;
+PrimaryExpression: GeneratorExpression;
+PrimaryExpression: RegularExpressionLiteral;
+PrimaryExpression: TemplateLiteral;
+
+PrimaryExpression: CoverParenthesizedExpressionAndArrowParameterList;
/.
-case $rule_number: {
- AST::NullExpression *node = new (pool) AST::NullExpression();
- node->nullToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (coverExpressionType != CE_ParenthesizedExpression) {
+ syntaxError(coverExpressionErrorLocation, "Expected token ')'.");
+ return false;
+ }
+ } break;
./
-PrimaryExpression: T_TRUE ;
+-- Parsing of the CoverParenthesizedExpressionAndArrowParameterList is restricted to the one rule below when this is parsed as a primary expression
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_RPAREN;
/.
-case $rule_number: {
- AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
- node->trueToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
+ node->lparenToken = loc(1);
+ node->rparenToken = loc(3);
+ sym(1).Node = node;
+ coverExpressionType = CE_ParenthesizedExpression;
+ } break;
./
-PrimaryExpression: T_FALSE ;
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN T_RPAREN;
/.
-case $rule_number: {
- AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
- node->falseToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ coverExpressionErrorLocation = loc(2);
+ coverExpressionType = CE_FormalParameterList;
+ } break;
./
-PrimaryExpression: T_NUMERIC_LITERAL ;
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN BindingRestElement T_RPAREN;
/.
-case $rule_number: {
- AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(2).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ coverExpressionErrorLocation = loc(2);
+ coverExpressionType = CE_FormalParameterList;
+ } break;
./
-PrimaryExpression: T_MULTILINE_STRING_LITERAL ;
-/.case $rule_number:./
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_COMMA BindingRestElementOpt T_RPAREN;
+/.
+ case $rule_number: {
+ AST::FormalParameterList *list = sym(2).Expression->reparseAsFormalParameterList(pool);
+ if (!list) {
+ syntaxError(loc(1), "Invalid Arrow parameter list.");
+ return false;
+ }
+ if (sym(4).Node) {
+ list = new (pool) AST::FormalParameterList(list, sym(4).PatternElement);
+ }
+ coverExpressionErrorLocation = loc(4);
+ coverExpressionType = CE_FormalParameterList;
+ sym(1).Node = list->finish(pool);
+ } break;
+./
-PrimaryExpression: T_STRING_LITERAL ;
+Literal: T_NULL;
/.
-case $rule_number: {
- AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NullExpression *node = new (pool) AST::NullExpression();
+ node->nullToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
+
+Literal: T_TRUE;
+/.
+ case $rule_number: {
+ AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
+ node->trueToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
+
+Literal: T_FALSE;
+/.
+ case $rule_number: {
+ AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
+ node->falseToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
+
+Literal: T_NUMERIC_LITERAL;
+/.
+ case $rule_number: {
+ AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_DIVIDE_ ;
+Literal: T_MULTILINE_STRING_LITERAL;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
+Literal: T_STRING_LITERAL;
+/.
+ case $rule_number: {
+ AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
+
+RegularExpressionLiteral: T_DIVIDE_;
/:
#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number
:/
/.
-case $rule_number: {
- bool rx = lexer->scanRegExp(Lexer::NoPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false; // ### remove me
- }
-
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
-
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+{
+ Lexer::RegExpBodyPrefix prefix;
+ case $rule_number:
+ prefix = Lexer::NoPrefix;
+ goto scan_regexp;
./
-PrimaryExpression: T_DIVIDE_EQ ;
+RegularExpressionLiteral: T_DIVIDE_EQ;
/:
#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number
:/
/.
-case $rule_number: {
- bool rx = lexer->scanRegExp(Lexer::EqualPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false;
- }
+ case $rule_number:
+ prefix = Lexer::EqualPrefix;
+ goto scan_regexp;
+
+ scan_regexp: {
+ bool rx = lexer->scanRegExp(prefix);
+ if (!rx) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
+ return false;
+ }
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
+ loc(1).length = lexer->tokenLength();
+ yylloc = loc(1); // adjust the location of the current token
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+ AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
+}
./
-PrimaryExpression: T_LBRACKET T_RBRACKET ;
+
+ArrayLiteral: T_LBRACKET ElisionOpt T_RBRACKET;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0);
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElementList *list = nullptr;
+ if (sym(2).Elision)
+ list = (new (pool) AST::PatternElementList(sym(2).Elision, nullptr))->finish();
+ AST::ArrayPattern *node = new (pool) AST::ArrayPattern(list);
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_LBRACKET Elision T_RBRACKET ;
+ArrayLiteral: T_LBRACKET ElementList T_RBRACKET;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArrayPattern *node = new (pool) AST::ArrayPattern(sym(2).PatternElementList->finish());
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ;
+ArrayLiteral: T_LBRACKET ElementList T_COMMA ElisionOpt T_RBRACKET;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ auto *list = sym(2).PatternElementList;
+ if (sym(4).Elision) {
+ AST::PatternElementList *l = new (pool) AST::PatternElementList(sym(4).Elision, nullptr);
+ list = list->append(l);
+ }
+ AST::ArrayPattern *node = new (pool) AST::ArrayPattern(list->finish());
+ node->lbracketToken = loc(1);
+ node->commaToken = loc(3);
+ node->rbracketToken = loc(5);
+ sym(1).Node = node;
+ Q_ASSERT(node->isValidArrayLiteral());
+ } break;
./
-PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ;
+ElementList: AssignmentExpression_In;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- (AST::Elision *) 0);
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(sym(1).Expression);
+ sym(1).Node = new (pool) AST::PatternElementList(nullptr, e);
+ } break;
./
-PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ;
+ElementList: Elision AssignmentExpression_In;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- sym(4).Elision->finish());
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(sym(2).Expression);
+ sym(1).Node = new (pool) AST::PatternElementList(sym(1).Elision->finish(), e);
+ } break;
./
--- PrimaryExpression: T_LBRACE T_RBRACE ;
--- /.
--- case $rule_number: {
--- sym(1).Node = new (pool) AST::ObjectLiteral();
--- } break;
--- ./
+ElementList: ElisionOpt SpreadElement;
+/.
+ case $rule_number: {
+ AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement);
+ sym(1).Node = node;
+ } break;
+./
-PrimaryExpression: T_LBRACE PropertyAssignmentListOpt T_RBRACE ;
+ElementList: ElementList T_COMMA ElisionOpt AssignmentExpression_In;
/.
-case $rule_number: {
- AST::ObjectLiteral *node = 0;
- if (sym(2).Node)
- node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- else
- node = new (pool) AST::ObjectLiteral();
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(sym(4).Expression);
+ AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(3).Elision, e);
+ sym(1).Node = sym(1).PatternElementList->append(node);
+ } break;
./
-PrimaryExpression: T_LBRACE PropertyAssignmentList T_COMMA T_RBRACE ;
+ElementList: ElementList T_COMMA ElisionOpt SpreadElement;
/.
-case $rule_number: {
- AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(3).Elision, sym(4).PatternElement);
+ sym(1).Node = sym(1).PatternElementList->append(node);
+ } break;
./
-PrimaryExpression: T_LPAREN Expression T_RPAREN ;
+Elision: T_COMMA;
/.
-case $rule_number: {
- AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
- node->lparenToken = loc(1);
- node->rparenToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Elision *node = new (pool) AST::Elision();
+ node->commaToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
+Elision: Elision T_COMMA;
+/.
+ case $rule_number: {
+ AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
-UiQualifiedId: MemberExpression ;
+ElisionOpt: ;
/.
-case $rule_number: {
- if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
- QLatin1String("Ignored annotation")));
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
+./
- sym(1).Expression = mem->base;
- }
+ElisionOpt: Elision;
+/.
+ case $rule_number: {
+ sym(1).Node = sym(1).Elision->finish();
+ } break;
+./
- if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
- sym(1).UiQualifiedId = qualifiedId;
- } else {
- sym(1).UiQualifiedId = 0;
+SpreadElement: T_ELLIPSIS AssignmentExpression;
+/.
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(2).Expression, AST::PatternElement::SpreadElement);
+ sym(1).Node = node;
+ } break;
+./
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
+ObjectLiteral: T_LBRACE T_RBRACE;
+/.
+ case $rule_number: {
+ AST::ObjectPattern *node = new (pool) AST::ObjectPattern();
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
- return false; // ### recover
- }
-} break;
+ObjectLiteral: T_LBRACE PropertyDefinitionList T_RBRACE;
+/.
+ case $rule_number: {
+ AST::ObjectPattern *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-ElementList: AssignmentExpression ;
+ObjectLiteral: T_LBRACE PropertyDefinitionList T_COMMA T_RBRACE;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression);
-} break;
+ case $rule_number: {
+ AST::ObjectPattern *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-ElementList: Elision AssignmentExpression ;
+
+UiPropertyDefinitionList: UiPropertyDefinition;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+PropertyDefinitionList: PropertyDefinition;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternProperty);
+ } break;
./
-ElementList: ElementList T_COMMA AssignmentExpression ;
+UiPropertyDefinitionList: UiPropertyDefinitionList T_COMMA UiPropertyDefinition;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+PropertyDefinitionList: PropertyDefinitionList T_COMMA PropertyDefinition;
/.
-case $rule_number: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList,
- (AST::Elision *) 0, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternPropertyList *node = new (pool) AST::PatternPropertyList(sym(1).PatternPropertyList, sym(3).PatternProperty);
+ sym(1).Node = node;
+ } break;
./
-ElementList: ElementList T_COMMA Elision AssignmentExpression ;
+PropertyDefinition: IdentifierReference;
/.
-case $rule_number: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(),
- sym(4).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ name->propertyNameToken = loc(1);
+ AST::IdentifierExpression *expr = new (pool) AST::IdentifierExpression(stringRef(1));
+ expr->identifierToken = loc(1);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(name, expr);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-Elision: T_COMMA ;
+-- Using this production should result in a syntax error when used in an ObjectLiteral
+PropertyDefinition: CoverInitializedName;
+
+CoverInitializedName: IdentifierReference Initializer_In;
/.
-case $rule_number: {
- AST::Elision *node = new (pool) AST::Elision();
- node->commaToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ name->propertyNameToken = loc(1);
+ AST::IdentifierExpression *left = new (pool) AST::IdentifierExpression(stringRef(1));
+ left->identifierToken = loc(1);
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ AST::BinaryExpression *assignment = new (pool) AST::BinaryExpression(left, QSOperator::Assign, sym(2).Expression);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(name, assignment);
+ node->colonToken = loc(1);
+ sym(1).Node = node;
+
+ } break;
./
-Elision: Elision T_COMMA ;
+UiPropertyDefinition: UiPropertyName T_COLON AssignmentExpression_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+PropertyDefinition: PropertyName T_COLON AssignmentExpression_In;
/.
-case $rule_number: {
- AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Expression);
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) {
+ if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName))
+ f->name = driver->newStringRef(sym(1).PropertyName->asString());
+ }
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) {
+ if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName))
+ c->name = driver->newStringRef(sym(1).PropertyName->asString());
+ }
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+PropertyDefinition: MethodDefinition;
+
+PropertyName: LiteralPropertyName;
+PropertyName: ComputedPropertyName;
+
+LiteralPropertyName: IdentifierName;
+/.
+ case $rule_number: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PropertyAssignment: PropertyName T_COLON AssignmentExpression ;
+UiPropertyName: T_STRING_LITERAL;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LiteralPropertyName: T_STRING_LITERAL;
/.
-case $rule_number: {
- AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue(
- sym(1).PropertyName, sym(3).Expression);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PropertyAssignment: T_GET PropertyName T_LPAREN T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+UiPropertyName: T_NUMERIC_LITERAL;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LiteralPropertyName: T_NUMERIC_LITERAL;
/.
-case $rule_number: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(6).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PropertyAssignment: T_SET PropertyName T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+IdentifierName: IdentifierReference;
+IdentifierName: ReservedIdentifier;
+
+ReservedIdentifier: T_BREAK;
+ReservedIdentifier: T_CASE;
+ReservedIdentifier: T_CATCH;
+ReservedIdentifier: T_CONTINUE;
+ReservedIdentifier: T_DEFAULT;
+ReservedIdentifier: T_DELETE;
+ReservedIdentifier: T_DO;
+ReservedIdentifier: T_ELSE;
+ReservedIdentifier: T_ENUM;
+ReservedIdentifier: T_FALSE;
+ReservedIdentifier: T_FINALLY;
+ReservedIdentifier: T_FOR;
+ReservedIdentifier: T_FUNCTION;
+ReservedIdentifier: T_IF;
+ReservedIdentifier: T_IN;
+ReservedIdentifier: T_INSTANCEOF;
+ReservedIdentifier: T_NEW;
+ReservedIdentifier: T_NULL;
+ReservedIdentifier: T_RETURN;
+ReservedIdentifier: T_SWITCH;
+ReservedIdentifier: T_THIS;
+ReservedIdentifier: T_THROW;
+ReservedIdentifier: T_TRUE;
+ReservedIdentifier: T_TRY;
+ReservedIdentifier: T_TYPEOF;
+ReservedIdentifier: T_VAR;
+ReservedIdentifier: T_VOID;
+ReservedIdentifier: T_WHILE;
+ReservedIdentifier: T_CONST;
+ReservedIdentifier: T_LET;
+ReservedIdentifier: T_DEBUGGER;
+ReservedIdentifier: T_RESERVED_WORD;
+ReservedIdentifier: T_SUPER;
+ReservedIdentifier: T_WITH;
+ReservedIdentifier: T_CLASS;
+ReservedIdentifier: T_EXTENDS;
+ReservedIdentifier: T_EXPORT;
+
+ComputedPropertyName: T_LBRACKET AssignmentExpression_In T_RBRACKET;
/.
-case $rule_number: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ComputedPropertyName *node = new (pool) AST::ComputedPropertyName(sym(2).Expression);
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PropertyAssignmentList: PropertyAssignment ;
+Initializer: T_EQ AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Initializer_In: T_EQ AssignmentExpression_In;
/.
case $rule_number: {
- sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment);
+ sym(1) = sym(2);
} break;
./
-PropertyAssignmentList: PropertyAssignmentList T_COMMA PropertyAssignment ;
+
+InitializerOpt: ;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+InitializerOpt_In: ;
/.
-case $rule_number: {
- AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList(
- sym(1).PropertyAssignmentList, sym(3).PropertyAssignment);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-PropertyName: JsIdentifier %prec SHIFT_THERE ;
+InitializerOpt: Initializer;
+InitializerOpt_In: Initializer_In;
+
+TemplateLiteral: T_NO_SUBSTITUTION_TEMPLATE;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
+TemplateSpans: T_TEMPLATE_TAIL;
/.
-case $rule_number: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), nullptr);
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PropertyName: T_STRING_LITERAL ;
+TemplateSpans: T_TEMPLATE_MIDDLE Expression TemplateSpans;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
+TemplateLiteral: T_TEMPLATE_HEAD Expression TemplateSpans;
/.
-case $rule_number: {
- AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), sym(2).Expression);
+ node->next = sym(3).Template;
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PropertyName: T_NUMERIC_LITERAL ;
+
+MemberExpression: PrimaryExpression;
+
+Super: T_SUPER;
/.
-case $rule_number: {
- AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::SuperLiteral *node = new (pool) AST::SuperLiteral();
+ node->superToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PropertyName: ReservedIdentifier ;
+
+MemberExpression: Super T_LBRACKET Expression_In T_RBRACKET;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+MemberExpression: MemberExpression T_LBRACKET Expression_In T_RBRACKET;
/.
-case $rule_number: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-ReservedIdentifier: T_BREAK ;
-ReservedIdentifier: T_CASE ;
-ReservedIdentifier: T_CATCH ;
-ReservedIdentifier: T_CONTINUE ;
-ReservedIdentifier: T_DEFAULT ;
-ReservedIdentifier: T_DELETE ;
-ReservedIdentifier: T_DO ;
-ReservedIdentifier: T_ELSE ;
-ReservedIdentifier: T_ENUM ;
-ReservedIdentifier: T_FALSE ;
-ReservedIdentifier: T_FINALLY ;
-ReservedIdentifier: T_FOR ;
-ReservedIdentifier: T_FUNCTION ;
-ReservedIdentifier: T_IF ;
-ReservedIdentifier: T_IN ;
-ReservedIdentifier: T_INSTANCEOF ;
-ReservedIdentifier: T_NEW ;
-ReservedIdentifier: T_NULL ;
-ReservedIdentifier: T_RETURN ;
-ReservedIdentifier: T_SWITCH ;
-ReservedIdentifier: T_THIS ;
-ReservedIdentifier: T_THROW ;
-ReservedIdentifier: T_TRUE ;
-ReservedIdentifier: T_TRY ;
-ReservedIdentifier: T_TYPEOF ;
-ReservedIdentifier: T_VAR ;
-ReservedIdentifier: T_VOID ;
-ReservedIdentifier: T_WHILE ;
-ReservedIdentifier: T_CONST ;
-ReservedIdentifier: T_LET ;
-ReservedIdentifier: T_DEBUGGER ;
-ReservedIdentifier: T_RESERVED_WORD ;
-ReservedIdentifier: T_WITH ;
-
-PropertyIdentifier: JsIdentifier ;
-PropertyIdentifier: ReservedIdentifier ;
-
-MemberExpression: PrimaryExpression ;
-MemberExpression: FunctionExpression ;
-
-MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ;
+
+-- the identifier has to be "target", catched at codegen time
+NewTarget: T_NEW T_DOT T_IDENTIFIER;
+/. case $rule_number:
+ {
+ AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
+ node->identifierToken= loc(1);
+ sym(1).Node = node;
+ } Q_FALLTHROUGH();
+./
+MemberExpression: Super T_DOT IdentifierName;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+MemberExpression: MemberExpression T_DOT IdentifierName;
/.
-case $rule_number: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-MemberExpression: MemberExpression T_DOT PropertyIdentifier ;
+MemberExpression: MetaProperty;
+
+MemberExpression: T_NEW MemberExpression T_LPAREN Arguments T_RPAREN;
/.
-case $rule_number: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
+ node->newToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+MetaProperty: NewTarget;
+
+
+NewExpression: MemberExpression;
+
+NewExpression: T_NEW NewExpression;
/.
-case $rule_number: {
- AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
- node->newToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
+ node->newToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-NewExpression: MemberExpression ;
-NewExpression: T_NEW NewExpression ;
+CallExpression: CallExpression TemplateLiteral;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+MemberExpression: MemberExpression TemplateLiteral;
/.
-case $rule_number: {
- AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
- node->newToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TaggedTemplate *node = new (pool) AST::TaggedTemplate(sym(1).Expression, sym(2).Template);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+CallExpression: MemberExpression T_LPAREN Arguments T_RPAREN;
/.
-case $rule_number: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+CallExpression: Super T_LPAREN Arguments T_RPAREN;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+CallExpression: CallExpression T_LPAREN Arguments T_RPAREN;
/.
-case $rule_number: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ;
+CallExpression: CallExpression T_LBRACKET Expression_In T_RBRACKET;
/.
-case $rule_number: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: CallExpression T_DOT PropertyIdentifier ;
+CallExpression: CallExpression T_DOT IdentifierName;
/.
-case $rule_number: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-ArgumentListOpt: ;
+Arguments: ;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-ArgumentListOpt: ArgumentList ;
+Arguments: ArgumentList;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Arguments: ArgumentList T_COMMA;
/.
-case $rule_number: {
- sym(1).Node = sym(1).ArgumentList->finish();
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).ArgumentList->finish();
+ } break;
./
-ArgumentList: AssignmentExpression ;
+ArgumentList: AssignmentExpression_In;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
+ } break;
./
-ArgumentList: ArgumentList T_COMMA AssignmentExpression ;
+ArgumentList: T_ELLIPSIS AssignmentExpression_In;
/.
-case $rule_number: {
- AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(2).Expression);
+ node->isSpreadElement = true;
+ sym(1).Node = node;
+ } break;
./
-LeftHandSideExpression: NewExpression ;
-LeftHandSideExpression: CallExpression ;
-PostfixExpression: LeftHandSideExpression ;
+ArgumentList: ArgumentList T_COMMA AssignmentExpression_In;
+/.
+ case $rule_number: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
-PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ;
+ArgumentList: ArgumentList T_COMMA T_ELLIPSIS AssignmentExpression_In;
/.
-case $rule_number: {
- AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
- node->incrementToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(4).Expression);
+ node->commaToken = loc(2);
+ node->isSpreadElement = true;
+ sym(1).Node = node;
+ } break;
./
-PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ;
+LeftHandSideExpression: NewExpression;
+LeftHandSideExpression: CallExpression;
+
+UpdateExpression: LeftHandSideExpression;
+
+UpdateExpression: LeftHandSideExpression T_PLUS_PLUS;
/.
-case $rule_number: {
- AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
- node->decrementToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
+ node->incrementToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: PostfixExpression ;
+UpdateExpression: LeftHandSideExpression T_MINUS_MINUS;
+/.
+ case $rule_number: {
+ AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
+ node->decrementToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
-UnaryExpression: T_DELETE UnaryExpression ;
+UpdateExpression: T_PLUS_PLUS UnaryExpression;
/.
-case $rule_number: {
- AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
- node->deleteToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
+ node->incrementToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_VOID UnaryExpression ;
+UpdateExpression: T_MINUS_MINUS UnaryExpression;
/.
-case $rule_number: {
- AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
- node->voidToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
+ node->decrementToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_TYPEOF UnaryExpression ;
+UnaryExpression: UpdateExpression;
+
+UnaryExpression: T_DELETE UnaryExpression;
/.
-case $rule_number: {
- AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
- node->typeofToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
+ node->deleteToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_PLUS_PLUS UnaryExpression ;
+UnaryExpression: T_VOID UnaryExpression;
/.
-case $rule_number: {
- AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
- node->incrementToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
+ node->voidToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_MINUS_MINUS UnaryExpression ;
+UnaryExpression: T_TYPEOF UnaryExpression;
/.
-case $rule_number: {
- AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
- node->decrementToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
+ node->typeofToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_PLUS UnaryExpression ;
+UnaryExpression: T_PLUS UnaryExpression;
/.
-case $rule_number: {
- AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
- node->plusToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
+ node->plusToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_MINUS UnaryExpression ;
+UnaryExpression: T_MINUS UnaryExpression;
/.
-case $rule_number: {
- AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
- node->minusToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
+ node->minusToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_TILDE UnaryExpression ;
+UnaryExpression: T_TILDE UnaryExpression;
/.
-case $rule_number: {
- AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
- node->tildeToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
+ node->tildeToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_NOT UnaryExpression ;
+UnaryExpression: T_NOT UnaryExpression;
/.
-case $rule_number: {
- AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
- node->notToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
+ node->notToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-MultiplicativeExpression: UnaryExpression ;
+ExponentiationExpression: UnaryExpression;
-MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ;
+ExponentiationExpression: UpdateExpression T_STAR_STAR ExponentiationExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mul, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Exp, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ;
+MultiplicativeExpression: ExponentiationExpression;
+
+MultiplicativeExpression: MultiplicativeExpression MultiplicativeOperator ExponentiationExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Div, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ;
+MultiplicativeOperator: T_STAR;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mod, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Mul;
+ } break;
./
-AdditiveExpression: MultiplicativeExpression ;
+MultiplicativeOperator: T_DIVIDE_;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Div;
+ } break;
+./
-AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ;
+MultiplicativeOperator: T_REMAINDER;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Add, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Mod;
+ } break;
./
-AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ;
+AdditiveExpression: MultiplicativeExpression;
+
+AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Sub, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression;
+/.
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ShiftExpression: AdditiveExpression ;
+ShiftExpression: AdditiveExpression;
-ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ;
+ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::LShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ;
+ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::RShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ;
+ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::URShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpression: ShiftExpression ;
+RelationalExpression_In: ShiftExpression;
+RelationalExpression: ShiftExpression;
-RelationalExpression: RelationalExpression T_LT ShiftExpression ;
+RelationalExpression_In: RelationalExpression_In RelationalOperator ShiftExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+RelationalExpression: RelationalExpression RelationalOperator ShiftExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpression: RelationalExpression T_GT ShiftExpression ;
+RelationalOperator: T_LT;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Lt;
+ } break;
+./
+RelationalOperator: T_GT;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Gt;
+ } break;
+./
+RelationalOperator: T_LE;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Le;
+ } break;
+./
+RelationalOperator: T_GE;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Ge;
+ } break;
+./
+RelationalOperator: T_INSTANCEOF;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InstanceOf;
+ } break;
./
-RelationalExpression: RelationalExpression T_LE ShiftExpression ;
+RelationalExpression_In: RelationalExpression_In T_IN ShiftExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpression: RelationalExpression T_GE ShiftExpression ;
+EqualityExpression_In: RelationalExpression_In;
+EqualityExpression: RelationalExpression;
+
+EqualityExpression_In: EqualityExpression_In EqualityOperator RelationalExpression_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+EqualityExpression: EqualityExpression EqualityOperator RelationalExpression;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ;
+EqualityOperator: T_EQ_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Equal;
+ } break;
+./
+EqualityOperator: T_NOT_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::NotEqual;
+ } break;
./
+EqualityOperator: T_EQ_EQ_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::StrictEqual;
+ } break;
+./
+EqualityOperator: T_NOT_EQ_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::StrictNotEqual;
+ } break;
+./
+
+
+BitwiseANDExpression: EqualityExpression;
+XitwiseANDExpression_In: EqualityExpression_In;
-RelationalExpression: RelationalExpression T_IN ShiftExpression ;
+BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+XitwiseANDExpression_In: XitwiseANDExpression_In T_AND EqualityExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::In, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpressionNotIn: ShiftExpression ;
-RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ;
+BitwiseXORExpression: BitwiseANDExpression;
+BitwiseXORExpression_In: XitwiseANDExpression_In;
+
+BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BitwiseXORExpression_In: BitwiseXORExpression_In T_XOR XitwiseANDExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ;
+BitwiseORExpression: BitwiseXORExpression;
+BitwiseORExpression_In: BitwiseXORExpression_In;
+
+BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BitwiseORExpression_In: BitwiseORExpression_In T_OR BitwiseXORExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ;
+LogicalANDExpression: BitwiseORExpression;
+LogicalANDExpression_In: BitwiseORExpression_In;
+
+LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LogicalANDExpression_In: LogicalANDExpression_In T_AND_AND BitwiseORExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ;
+LogicalORExpression: LogicalANDExpression;
+LogicalORExpression_In: LogicalANDExpression_In;
+
+LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LogicalORExpression_In: LogicalORExpression_In T_OR_OR LogicalANDExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ;
+
+ConditionalExpression: LogicalORExpression;
+ConditionalExpression_In: LogicalORExpression_In;
+
+ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression_In T_COLON AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ConditionalExpression_In: LogicalORExpression_In T_QUESTION AssignmentExpression_In T_COLON AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression);
+ node->questionToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-EqualityExpression: RelationalExpression ;
+AssignmentExpression: ConditionalExpression;
+AssignmentExpression_In: ConditionalExpression_In;
+
+AssignmentExpression: YieldExpression;
+AssignmentExpression_In: YieldExpression_In;
-EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ;
+AssignmentExpression: ArrowFunction;
+AssignmentExpression_In: ArrowFunction_In;
+
+AssignmentExpression: LeftHandSideExpression T_EQ AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+AssignmentExpression_In: LeftHandSideExpression T_EQ AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ // need to convert the LHS to an AssignmentPattern if it was an Array/ObjectLiteral
+ if (AST::Pattern *p = sym(1).Expression->patternCast()) {
+ AST::SourceLocation errorLoc;
+ QString errorMsg;
+ if (!p->convertLiteralToAssignmentPattern(pool, &errorLoc, &errorMsg)) {
+ syntaxError(errorLoc, errorMsg);
+ return false;
+ }
+ }
+ // if lhs is an identifier expression and rhs is an anonymous function expression, we need to assign the name of lhs to the function
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) {
+ if (auto *id = AST::cast<AST::IdentifierExpression *>(sym(1).Expression))
+ f->name = id->name;
+ }
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) {
+ if (auto *id = AST::cast<AST::IdentifierExpression *>(sym(1).Expression))
+ c->name = id->name;
+ }
+
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Assign, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ;
+AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+AssignmentExpression_In: LeftHandSideExpression AssignmentOperator AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ;
+AssignmentOperator: T_STAR_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceMul;
+ } break;
./
-EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ;
+AssignmentOperator: T_STAR_STAR_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceExp;
+ } break;
./
-EqualityExpressionNotIn: RelationalExpressionNotIn ;
+AssignmentOperator: T_DIVIDE_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceDiv;
+ } break;
+./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ;
+AssignmentOperator: T_REMAINDER_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceMod;
+ } break;
./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn;
+AssignmentOperator: T_PLUS_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceAdd;
+ } break;
./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ;
+AssignmentOperator: T_MINUS_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceSub;
+ } break;
./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ;
+AssignmentOperator: T_LT_LT_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceLeftShift;
+ } break;
./
-BitwiseANDExpression: EqualityExpression ;
+AssignmentOperator: T_GT_GT_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceRightShift;
+ } break;
+./
-BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ;
+AssignmentOperator: T_GT_GT_GT_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceURightShift;
+ } break;
./
-BitwiseANDExpressionNotIn: EqualityExpressionNotIn ;
+AssignmentOperator: T_AND_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceAnd;
+ } break;
+./
-BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ;
+AssignmentOperator: T_XOR_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceXor;
+ } break;
+./
+
+AssignmentOperator: T_OR_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceOr;
+ } break;
./
-BitwiseXORExpression: BitwiseANDExpression ;
+Expression: AssignmentExpression;
+Expression_In: AssignmentExpression_In;
-BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ;
+Expression: Expression T_COMMA AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Expression_In: Expression_In T_COMMA AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ;
+ExpressionOpt: ;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ExpressionOpt_In: ;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
+./
+
+ExpressionOpt: Expression;
+ExpressionOpt_In: Expression_In;
-BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ;
+-- Statements
+
+Statement: ExpressionStatementLookahead T_FORCE_BLOCK BlockStatement;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(3).Node;
+ } break;
./
-BitwiseORExpression: BitwiseXORExpression ;
+Statement: ExpressionStatementLookahead VariableStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead EmptyStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ExpressionStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead IfStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead BreakableStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ContinueStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead BreakStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ReturnStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead WithStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead LabelledStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ThrowStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead TryStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead DebuggerStatement;
+/.
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ } break;
+./
+
+Declaration: HoistableDeclaration;
+Declaration: ClassDeclaration;
+Declaration: LexicalDeclaration_In;
+
+HoistableDeclaration: FunctionDeclaration;
+HoistableDeclaration: GeneratorDeclaration;
+
+HoistableDeclaration_Default: FunctionDeclaration_Default;
+HoistableDeclaration_Default: GeneratorDeclaration_Default;
+
+BreakableStatement: IterationStatement;
+BreakableStatement: SwitchStatement;
-BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ;
+BlockStatement: Block;
+
+Block: T_LBRACE StatementListOpt T_RBRACE;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ;
+StatementList: StatementListItem;
-BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ;
+StatementList: StatementList StatementListItem;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).StatementList = sym(1).StatementList->append(sym(2).StatementList);
+ } break;
./
-LogicalANDExpression: BitwiseORExpression ;
+StatementListItem: Statement;
+/.
+ case $rule_number: {
+ sym(1).StatementList = new (pool) AST::StatementList(sym(1).Statement);
+ } break;
+./
-LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ;
+StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_AUTOMATIC_SEMICOLON;
+StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_SEMICOLON;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::StatementList(sym(3).FunctionDeclaration);
+ } break;
./
-LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ;
+StatementListOpt: ExpressionStatementLookahead;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
+./
-LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ;
+StatementListOpt: StatementList;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).StatementList->finish();
+ } break;
./
-LogicalORExpression: LogicalANDExpression ;
+LetOrConst: T_LET;
+/.
+ case $rule_number: {
+ sym(1).scope = AST::VariableScope::Let;
+ } break;
+./
+LetOrConst: T_CONST;
+/.
+ case $rule_number: {
+ sym(1).scope = AST::VariableScope::Const;
+ } break;
+./
-LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ;
+Var: T_VAR;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).scope = AST::VariableScope::Var;
+ } break;
./
-LogicalORExpressionNotIn: LogicalANDExpressionNotIn ;
+LexicalDeclaration: LetOrConst BindingList;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LexicalDeclaration_In: LetOrConst BindingList_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VarDeclaration: Var VariableDeclarationList;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VarDeclaration_In: Var VariableDeclarationList_In;
+/.
+ case $rule_number: {
+ AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(sym(1).scope));
+ node->declarationKindToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
+
+VariableStatement: VarDeclaration_In T_AUTOMATIC_SEMICOLON;
+VariableStatement: VarDeclaration_In T_SEMICOLON;
-LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ;
+BindingList: LexicalBinding_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BindingList_In: LexicalBinding_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList: VariableDeclaration;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList_In: VariableDeclaration_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).PatternElement);
+ } break;
./
-ConditionalExpression: LogicalORExpression ;
+BindingList: BindingList T_COMMA LexicalBinding;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BindingList_In: BindingList_In T_COMMA LexicalBinding_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList_In: VariableDeclarationList_In T_COMMA VariableDeclaration_In;
+/.
+ case $rule_number: {
+ AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).PatternElement);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
-ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ;
+LexicalBinding: BindingIdentifier InitializerOpt;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LexicalBinding_In: BindingIdentifier InitializerOpt_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration: BindingIdentifier InitializerOpt;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration_In: BindingIdentifier InitializerOpt_In;
/.
-case $rule_number: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ } break;
./
-ConditionalExpressionNotIn: LogicalORExpressionNotIn ;
+LexicalBinding: BindingPattern Initializer;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LexicalBinding_In: BindingPattern Initializer_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration: BindingPattern Initializer;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration_In: BindingPattern Initializer_In;
+/.
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(sym(1).Pattern, sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
-ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ;
+BindingPattern: T_LBRACE ObjectBindingPattern T_RBRACE;
/.
-case $rule_number: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ auto *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ node->parseMode = AST::Pattern::Binding;
+ sym(1).Node = node;
+ } break;
./
-AssignmentExpression: ConditionalExpression ;
+BindingPattern: T_LBRACKET ArrayBindingPattern T_RBRACKET;
+/.
+ case $rule_number: {
+ auto *node = new (pool) AST::ArrayPattern(sym(2).PatternElementList);
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ node->parseMode = AST::Pattern::Binding;
+ sym(1).Node = node;
+ } break;
+./
+
+ObjectBindingPattern: ;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
+./
+
+ObjectBindingPattern: BindingPropertyList;
+/. case $rule_number: ./
+ObjectBindingPattern: BindingPropertyList T_COMMA;
+/.
+ case $rule_number: {
+ sym(1).Node = sym(1).PatternPropertyList->finish();
+ } break;
+./
+
+ArrayBindingPattern: ElisionOpt BindingRestElementOpt;
+/.
+ case $rule_number: {
+ if (sym(1).Elision || sym(2).Node) {
+ auto *l = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement);
+ sym(1).Node = l->finish();
+ } else {
+ sym(1).Node = nullptr;
+ }
+ } break;
+./
-AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ;
+ArrayBindingPattern: BindingElementList;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).PatternElementList->finish();
+ } break;
./
-AssignmentExpressionNotIn: ConditionalExpressionNotIn ;
+ArrayBindingPattern: BindingElementList T_COMMA ElisionOpt BindingRestElementOpt;
+/.
+ case $rule_number: {
+ if (sym(3).Elision || sym(4).Node) {
+ auto *l = new (pool) AST::PatternElementList(sym(3).Elision, sym(4).PatternElement);
+ l = sym(1).PatternElementList->append(l);
+ sym(1).Node = l;
+ }
+ sym(1).Node = sym(1).PatternElementList->finish();
+ } break;
+./
-AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ;
+BindingPropertyList: BindingProperty;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternProperty);
+ } break;
./
-AssignmentOperator: T_EQ ;
+BindingPropertyList: BindingPropertyList T_COMMA BindingProperty;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::Assign;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternPropertyList, sym(3).PatternProperty);
+ } break;
./
-AssignmentOperator: T_STAR_EQ ;
+BindingElementList: BindingElisionElement;
+
+BindingElementList: BindingElementList T_COMMA BindingElisionElement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceMul;
-} break;
+ case $rule_number: {
+ sym(1).PatternElementList = sym(1).PatternElementList->append(sym(3).PatternElementList);
+ } break;
./
-AssignmentOperator: T_DIVIDE_EQ ;
+BindingElisionElement: ElisionOpt BindingElement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceDiv;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement);
+ } break;
./
-AssignmentOperator: T_REMAINDER_EQ ;
+
+BindingProperty: BindingIdentifier InitializerOpt_In;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceMod;
-} break;
+ case $rule_number: {
+ AST::StringLiteralPropertyName *name = new (pool) AST::StringLiteralPropertyName(stringRef(1));
+ name->propertyNameToken = loc(1);
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ sym(1).Node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression);
+ } break;
./
-AssignmentOperator: T_PLUS_EQ ;
+BindingProperty: PropertyName T_COLON BindingIdentifier InitializerOpt_In;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceAdd;
-} break;
+ case $rule_number: {
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, stringRef(3), sym(4).Expression);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_MINUS_EQ ;
+BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceSub;
-} break;
+ case $rule_number: {
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Pattern, sym(4).Expression);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_LT_LT_EQ ;
+BindingElement: BindingIdentifier InitializerOpt_In;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceLeftShift;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_GT_GT_EQ ;
+BindingElement: BindingPattern InitializerOpt_In;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceRightShift;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(1).Pattern, sym(2).Expression);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_GT_GT_GT_EQ ;
+BindingRestElement: T_ELLIPSIS BindingIdentifier;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceURightShift;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), nullptr, AST::PatternElement::RestElement);
+ node->identifierToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_AND_EQ ;
+BindingRestElement: T_ELLIPSIS BindingPattern;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceAnd;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(2).Pattern, nullptr, AST::PatternElement::RestElement);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_XOR_EQ ;
+BindingRestElementOpt: ;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceXor;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-AssignmentOperator: T_OR_EQ ;
+BindingRestElementOpt: BindingRestElement;
+
+
+EmptyStatement: T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceOr;
-} break;
+ case $rule_number: {
+ AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
+ node->semicolonToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-Expression: AssignmentExpression ;
+-- Spec says it should have a "[lookahead ∉ { {, function, class, let [ }]" before the Expression statement.
+-- This is implemented with the rule below that is run before any statement and inserts a T_EXPRESSION_STATEMENT_OK
+-- token if it's ok to parse as an expression statement.
+ExpressionStatementLookahead: ;
+/:
+#define J_SCRIPT_EXPRESSIONSTATEMENTLOOKAHEAD_RULE $rule_number
+:/
+/.
+ case $rule_number: {
+ int token = lookaheadToken(lexer);
+ if (token == T_LBRACE)
+ pushToken(T_FORCE_BLOCK);
+ else if (token == T_FUNCTION || token == T_CLASS || token == T_LET || token == T_CONST)
+ pushToken(T_FORCE_DECLARATION);
+ } break;
+./
-Expression: Expression T_COMMA AssignmentExpression ;
+ExpressionStatement: Expression_In T_AUTOMATIC_SEMICOLON;
+ExpressionStatement: Expression_In T_SEMICOLON;
/.
-case $rule_number: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ExpressionOpt: ;
+IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement T_ELSE Statement;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->elseToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-ExpressionOpt: Expression ;
+IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement;
+/.
+ case $rule_number: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
-ExpressionNotIn: AssignmentExpressionNotIn ;
-ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ;
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_AUTOMATIC_SEMICOLON;
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_COMPATIBILITY_SEMICOLON; -- for JSC/V8 compatibility
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_SEMICOLON;
/.
-case $rule_number: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
+ node->doToken = loc(1);
+ node->whileToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+ } break;
./
-ExpressionNotInOpt: ;
+IterationStatement: T_WHILE T_LPAREN Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
+ node->whileToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-ExpressionNotInOpt: ExpressionNotIn ;
+IterationStatement: T_FOR T_LPAREN ExpressionOpt T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement; -- [lookahead != { let [ }]
+/.
+ case $rule_number: {
+ AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->firstSemicolonToken = loc(4);
+ node->secondSemicolonToken = loc(6);
+ node->rparenToken = loc(8);
+ sym(1).Node = node;
+ } break;
+./
-Statement: Block ;
-Statement: VariableStatement ;
-Statement: EmptyStatement ;
-Statement: ExpressionStatement ;
-Statement: IfStatement ;
-Statement: IterationStatement ;
-Statement: ContinueStatement ;
-Statement: BreakStatement ;
-Statement: ReturnStatement ;
-Statement: WithStatement ;
-Statement: LabelledStatement ;
-Statement: SwitchStatement ;
-Statement: ThrowStatement ;
-Statement: TryStatement ;
-Statement: DebuggerStatement ;
+IterationStatement: T_FOR T_LPAREN VarDeclaration T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+IterationStatement: T_FOR T_LPAREN LexicalDeclaration T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement;
+/.
+ case $rule_number: {
+ // ### get rid of the static_cast!
+ AST::ForStatement *node = new (pool) AST::ForStatement(
+ static_cast<AST::VariableStatement *>(sym(3).Node)->declarations, sym(5).Expression,
+ sym(7).Expression, sym(9).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->firstSemicolonToken = loc(4);
+ node->secondSemicolonToken = loc(6);
+ node->rparenToken = loc(8);
+ sym(1).Node = node;
+ } break;
+./
+InOrOf: T_IN;
+/.
+ case $rule_number: {
+ sym(1).forEachType = AST::ForEachType::In;
+ } break;
+./
-Block: T_LBRACE StatementListOpt T_RBRACE ;
+InOrOf: T_OF;
/.
-case $rule_number: {
- AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).forEachType = AST::ForEachType::Of;
+ } break;
./
-StatementList: Statement ;
+IterationStatement: T_FOR T_LPAREN LeftHandSideExpression InOrOf Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).Statement);
-} break;
+ case $rule_number: {
+ // need to convert the LHS to an AssignmentPattern if it was an Array/ObjectLiteral
+ if (AST::Pattern *p = sym(3).Expression->patternCast()) {
+ AST::SourceLocation errorLoc;
+ QString errorMsg;
+ if (!p->convertLiteralToAssignmentPattern(pool, &errorLoc, &errorMsg)) {
+ syntaxError(errorLoc, errorMsg);
+ return false;
+ }
+ }
+ AST::ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->inOfToken = loc(4);
+ node->rparenToken = loc(6);
+ node->type = sym(4).forEachType;
+ sym(1).Node = node;
+ } break;
./
-StatementList: StatementList Statement ;
+IterationStatement: T_FOR T_LPAREN ForDeclaration InOrOf Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement);
-} break;
+ case $rule_number: {
+ AST::ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).PatternElement, sym(5).Expression, sym(7).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->inOfToken = loc(4);
+ node->rparenToken = loc(6);
+ node->type = sym(4).forEachType;
+ sym(1).Node = node;
+ } break;
./
-StatementListOpt: ;
+ForDeclaration: LetOrConst BindingIdentifier;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ForDeclaration: Var BindingIdentifier;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(stringRef(2), nullptr);
+ node->identifierToken = loc(2);
+ node->scope = sym(1).scope;
+ sym(1).Node = node;
+ } break;
./
-StatementListOpt: StatementList ;
+ForDeclaration: LetOrConst BindingPattern;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ForDeclaration: Var BindingPattern;
/.
-case $rule_number: {
- sym(1).Node = sym(1).StatementList->finish ();
-} break;
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(sym(2).Pattern, nullptr);
+ node->scope = sym(1).scope;
+ sym(1).Node = node;
+ } break;
./
-VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ;
+ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON;
+ContinueStatement: T_CONTINUE T_SEMICOLON;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- if (sym(1).ival == T_LET)
- s = AST::VariableDeclaration::BlockScope;
- else if (sym(1).ival == T_CONST)
- s = AST::VariableDeclaration::ReadOnlyBlockScope;
-
- AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s));
- node->declarationKindToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
+ node->continueToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationKind: T_LET ;
+ContinueStatement: T_CONTINUE IdentifierReference T_AUTOMATIC_SEMICOLON;
+ContinueStatement: T_CONTINUE IdentifierReference T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).ival = T_LET;
-} break;
+ case $rule_number: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
+ node->continueToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationKind: T_CONST ;
+BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON;
+BreakStatement: T_BREAK T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).ival = T_CONST;
-} break;
+ case $rule_number: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
+ node->breakToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationKind: T_VAR ;
+BreakStatement: T_BREAK IdentifierReference T_AUTOMATIC_SEMICOLON;
+BreakStatement: T_BREAK IdentifierReference T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).ival = T_VAR;
-} break;
+ case $rule_number: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
+ node->breakToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationList: VariableDeclaration ;
+ReturnStatement: T_RETURN ExpressionOpt_In T_AUTOMATIC_SEMICOLON;
+ReturnStatement: T_RETURN ExpressionOpt_In T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
+ case $rule_number: {
+ if (!functionNestingLevel) {
+ syntaxError(loc(1), "Return statement not allowed outside of Function declaration.");
+ return false;
+ }
+ AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
+ node->returnToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ;
+WithStatement: T_WITH T_LPAREN Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(
- sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
+ node->withToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationListNotIn: VariableDeclarationNotIn ;
+SwitchStatement: T_SWITCH T_LPAREN Expression_In T_RPAREN CaseBlock;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
+ case $rule_number: {
+ AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
+ node->switchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ;
+CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
-} break;
+ case $rule_number: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclaration: JsIdentifier InitialiserOpt ;
+CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ;
+CaseClauses: CaseClause;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
+ } break;
./
-Initialiser: T_EQ AssignmentExpression ;
+CaseClauses: CaseClauses CaseClause;
/.
-case $rule_number: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
+ } break;
./
-InitialiserOpt: ;
+CaseClausesOpt: ;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-InitialiserOpt: Initialiser ;
+CaseClausesOpt: CaseClauses;
+/.
+ case $rule_number: {
+ sym(1).Node = sym(1).CaseClauses->finish();
+ } break;
+./
-InitialiserNotIn: T_EQ AssignmentExpressionNotIn ;
+CaseClause: T_CASE Expression_In T_COLON StatementListOpt;
/.
-case $rule_number: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
+ case $rule_number: {
+ AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
+ node->caseToken = loc(1);
+ node->colonToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-InitialiserNotInOpt: ;
+DefaultClause: T_DEFAULT T_COLON StatementListOpt;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
+ node->defaultToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-InitialiserNotInOpt: InitialiserNotIn ;
+LabelledStatement: IdentifierReference T_COLON LabelledItem;
+/.
+ case $rule_number: {
+ AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
+ node->identifierToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+LabelledItem: Statement;
-EmptyStatement: T_SEMICOLON ;
+LabelledItem: ExpressionStatementLookahead T_FORCE_DECLARATION FunctionDeclaration;
/.
-case $rule_number: {
- AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
- node->semicolonToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ syntaxError(loc(3), "FunctionDeclarations are not allowed after a label.");
+ return false;
+ } break;
./
-ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ExpressionStatement: Expression T_SEMICOLON ;
+ThrowStatement: T_THROW Expression_In T_AUTOMATIC_SEMICOLON;
+ThrowStatement: T_THROW Expression_In T_SEMICOLON;
/.
-case $rule_number: {
- AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
+ node->throwToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ;
+TryStatement: T_TRY Block Catch;
/.
-case $rule_number: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->elseToken = loc(6);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ;
+TryStatement: T_TRY Block Finally;
/.
-case $rule_number: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
+TryStatement: T_TRY Block Catch Finally;
+/.
+ case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_COMPATIBILITY_SEMICOLON ; -- for JSC/V8 compatibility
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ;
+Catch: T_CATCH T_LPAREN CatchParameter T_RPAREN Block;
/.
-case $rule_number: {
- AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
- node->doToken = loc(1);
- node->whileToken = loc(3);
- node->lparenToken = loc(4);
- node->rparenToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Catch *node = new (pool) AST::Catch(sym(3).PatternElement, sym(5).Block);
+ node->catchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->identifierToken = loc(3);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ;
+Finally: T_FINALLY Block;
/.
-case $rule_number: {
- AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
- node->whileToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
+ node->finallyToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+CatchParameter: BindingIdentifier;
/.
-case $rule_number: {
- AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Expression, sym(9).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->firstSemicolonToken = loc(4);
- node->secondSemicolonToken = loc(6);
- node->rparenToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1));
+ node->identifierToken = loc(1);
+ node->scope = AST::VariableScope::Let;
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+CatchParameter: BindingPattern;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::LocalForStatement *node = new (pool) AST::LocalForStatement(
- sym(4).VariableDeclarationList->finish(s), sym(6).Expression,
- sym(8).Expression, sym(10).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->firstSemicolonToken = loc(5);
- node->secondSemicolonToken = loc(7);
- node->rparenToken = loc(9);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(1).Pattern);
+ node->scope = AST::VariableScope::Let;
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ;
+DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON; -- automatic semicolon
+DebuggerStatement: T_DEBUGGER T_SEMICOLON;
/.
-case $rule_number: {
- AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->inToken = loc(4);
- node->rparenToken = loc(6);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
+ node->debuggerToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ;
+-- tell the parser to prefer function declarations to function expressions.
+-- That is, the `Function' symbol is used to mark the start of a function
+-- declaration.
+-- This is still required for parsing QML, where MemberExpression and FunctionDeclaration would
+-- otherwise conflict.
+Function: T_FUNCTION %prec REDUCE_HERE;
+
+FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement(
- sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->inToken = loc(5);
- node->rparenToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+ } break;
./
-ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ContinueStatement: T_CONTINUE T_SEMICOLON ;
+
+FunctionDeclaration_Default: FunctionDeclaration;
+FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
- node->continueToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->lbraceToken = loc(5);
+ node->rbraceToken = loc(7);
+ sym(1).Node = node;
+ } break;
./
-ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ;
+FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
- node->continueToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ if (! stringRef(2).isNull())
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+ } break;
./
-BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-BreakStatement: T_BREAK T_SEMICOLON ;
+FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
- node->breakToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList);
+ node->functionToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->lbraceToken = loc(5);
+ node->rbraceToken = loc(7);
+ sym(1).Node = node;
+ } break;
./
-BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ;
+StrictFormalParameters: FormalParameters;
+
+FormalParameters: ;
/.
-case $rule_number: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
- node->breakToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ;
+FormalParameters: BindingRestElement;
/.
-case $rule_number: {
- AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
- node->returnToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ } break;
./
-WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ;
+FormalParameters: FormalParameterList;
+/. case $rule_number: ./
+FormalParameters: FormalParameterList T_COMMA;
/.
-case $rule_number: {
- AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
- node->withToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).FormalParameterList->finish(pool);
+ } break;
./
-SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ;
+FormalParameters: FormalParameterList T_COMMA BindingRestElement;
/.
-case $rule_number: {
- AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
- node->switchToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(sym(1).FormalParameterList, sym(3).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ } break;
./
-CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ;
+FormalParameterList: BindingElement;
/.
-case $rule_number: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement);
+ sym(1).Node = node;
+ } break;
./
-CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ;
+
+FormalParameterList: FormalParameterList T_COMMA BindingElement;
/.
-case $rule_number: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, sym(3).PatternElement);
+ sym(1).Node = node;
+ } break;
./
-CaseClauses: CaseClause ;
+FormalParameter: BindingElement;
+
+FunctionLBrace: T_LBRACE;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
-} break;
+ case $rule_number: {
+ ++functionNestingLevel;
+ } break;
./
-CaseClauses: CaseClauses CaseClause ;
+FunctionRBrace: T_RBRACE;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
-} break;
+ case $rule_number: {
+ --functionNestingLevel;
+ } break;
./
-CaseClausesOpt: ;
+
+FunctionBody: StatementListOpt;
+
+ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression; -- [lookahead ≠ {]
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression_In; -- [lookahead ≠ {]
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::ReturnStatement *ret = new (pool) AST::ReturnStatement(sym(4).Expression);
+ ret->returnToken = sym(4).Node->firstSourceLocation();
+ ret->semicolonToken = sym(4).Node->lastSourceLocation();
+ AST::StatementList *statements = (new (pool) AST::StatementList(ret))->finish();
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, statements);
+ f->isArrowFunction = true;
+ f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1);
+ f->lbraceToken = sym(4).Node->firstSourceLocation();
+ f->rbraceToken = sym(4).Node->lastSourceLocation();
+ sym(1).Node = f;
+ } break;
./
-CaseClausesOpt: CaseClauses ;
+ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- sym(1).Node = sym(1).CaseClauses->finish ();
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, sym(6).StatementList);
+ f->isArrowFunction = true;
+ f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(7);
+ sym(1).Node = f;
+ } break;
./
-CaseClause: T_CASE Expression T_COLON StatementListOpt ;
+ArrowParameters: BindingIdentifier;
/.
-case $rule_number: {
- AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
- node->caseToken = loc(1);
- node->colonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), nullptr, AST::PatternElement::Binding);
+ e->identifierToken = loc(1);
+ sym(1).FormalParameterList = (new (pool) AST::FormalParameterList(nullptr, e))->finish(pool);
+ } break;
./
-DefaultClause: T_DEFAULT T_COLON StatementListOpt ;
+-- CoverParenthesizedExpressionAndArrowParameterList for ArrowParameters i being refined to:
+-- ArrowFormalParameters: T_LPAREN StrictFormalParameters T_RPAREN
+ArrowParameters: CoverParenthesizedExpressionAndArrowParameterList;
/.
-case $rule_number: {
- AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
- node->defaultToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (coverExpressionType != CE_FormalParameterList) {
+ AST::NestedExpression *ne = static_cast<AST::NestedExpression *>(sym(1).Node);
+ AST::FormalParameterList *list = ne->expression->reparseAsFormalParameterList(pool);
+ if (!list) {
+ syntaxError(loc(1), "Invalid Arrow parameter list.");
+ return false;
+ }
+ sym(1).Node = list->finish(pool);
+ }
+ } break;
./
-LabelledStatement: JsIdentifier T_COLON Statement ;
+ConciseBodyLookahead: ;
+/:
+#define J_SCRIPT_CONCISEBODYLOOKAHEAD_RULE $rule_number
+:/
/.
-case $rule_number: {
- AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
- node->identifierToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (lookaheadToken(lexer) == T_LBRACE)
+ pushToken(T_FORCE_BLOCK);
+ } break;
./
-ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ThrowStatement: T_THROW Expression T_SEMICOLON ;
+MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
- node->throwToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList);
+ f->functionToken = sym(1).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(2);
+ f->rparenToken = loc(4);
+ f->lbraceToken = loc(5);
+ f->rbraceToken = loc(7);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, f);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-TryStatement: T_TRY Block Catch ;
+MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ f->functionToken = sym(2).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(3);
+ f->rparenToken = loc(5);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
+ f->isGenerator = true;
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+
+MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+/.
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(6).StatementList);
+ f->functionToken = sym(2).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(3);
+ f->rparenToken = loc(4);
+ f->lbraceToken = loc(5);
+ f->rbraceToken = loc(7);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+/.
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ f->functionToken = sym(2).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(3);
+ f->rparenToken = loc(5);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-TryStatement: T_TRY Block Finally ;
+
+PropertySetParameterList: FormalParameter;
/.
-case $rule_number: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ } break;
+./
+
+GeneratorLParen: T_LPAREN;
+/.
+ case $rule_number: {
+ lexer->enterGeneratorBody();
+ } break;
./
-TryStatement: T_TRY Block Catch Finally ;
+GeneratorRBrace: T_RBRACE;
/.
-case $rule_number: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ --functionNestingLevel;
+ lexer->leaveGeneratorBody();
+ } break;
./
-Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ;
+GeneratorDeclaration: Function T_STAR BindingIdentifier GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block);
- node->catchToken = loc(1);
- node->lparenToken = loc(2);
- node->identifierToken = loc(3);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(3), sym(5).FormalParameterList, sym(8).StatementList);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
+./
+
+GeneratorDeclaration_Default: GeneratorDeclaration;
+GeneratorDeclaration_Default: Function T_STAR GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
+/.
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(1), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
./
-Finally: T_FINALLY Block ;
+GeneratorExpression: T_FUNCTION T_STAR BindingIdentifier GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
- node->finallyToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(3), sym(5).FormalParameterList, sym(8).StatementList);
+ node->functionToken = loc(1);
+ if (!stringRef(3).isNull())
+ node->identifierToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
./
-DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-DebuggerStatement: T_DEBUGGER T_SEMICOLON ;
+GeneratorExpression: T_FUNCTION T_STAR GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
- node->debuggerToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
./
--- tell the parser to prefer function declarations to function expressions.
--- That is, the `Function' symbol is used to mark the start of a function
--- declaration.
-Function: T_FUNCTION %prec REDUCE_HERE ;
+GeneratorBody: FunctionBody;
-FunctionDeclaration: Function JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+YieldExpression: T_YIELD;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+YieldExpression_In: T_YIELD;
/.
-case $rule_number: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::YieldExpression *node = new (pool) AST::YieldExpression();
+ node->yieldToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-FunctionExpression: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+YieldExpression: T_YIELD T_STAR AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+YieldExpression_In: T_YIELD T_STAR AssignmentExpression_In;
/.
-case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- if (! stringRef(2).isNull())
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::YieldExpression *node = new (pool) AST::YieldExpression(sym(3).Expression);
+ node->yieldToken = loc(1);
+ node->isYieldStar = true;
+ sym(1).Node = node;
+ } break;
./
-FunctionExpression: T_FUNCTION T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+YieldExpression: T_YIELD AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+YieldExpression_In: T_YIELD AssignmentExpression_In;
/.
-case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody);
- node->functionToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::YieldExpression *node = new (pool) AST::YieldExpression(sym(2).Expression);
+ node->yieldToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-FormalParameterList: JsIdentifier ;
+
+ClassDeclaration: T_CLASS BindingIdentifier ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ClassDeclaration *node = new (pool) AST::ClassDeclaration(stringRef(2), sym(3).Expression, sym(5).ClassElementList);
+ node->classToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lbraceToken = loc(4);
+ node->rbraceToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-FormalParameterList: FormalParameterList T_COMMA JsIdentifier ;
+ClassExpression: T_CLASS BindingIdentifier ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3));
- node->commaToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ClassExpression *node = new (pool) AST::ClassExpression(stringRef(2), sym(3).Expression, sym(5).ClassElementList);
+ node->classToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lbraceToken = loc(4);
+ node->rbraceToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-FormalParameterListOpt: ;
+ClassDeclaration_Default: T_CLASS ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::ClassDeclaration *node = new (pool) AST::ClassDeclaration(QStringRef(), sym(2).Expression, sym(4).ClassElementList);
+ node->classToken = loc(1);
+ node->lbraceToken = loc(3);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-FormalParameterListOpt: FormalParameterList ;
+ClassExpression: T_CLASS ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- sym(1).Node = sym(1).FormalParameterList->finish ();
-} break;
+ case $rule_number: {
+ AST::ClassExpression *node = new (pool) AST::ClassExpression(QStringRef(), sym(2).Expression, sym(4).ClassElementList);
+ node->classToken = loc(1);
+ node->lbraceToken = loc(3);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-FunctionBodyOpt: ;
+ClassDeclaration_Default: ClassDeclaration;
+
+ClassLBrace: T_LBRACE;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ lexer->setStaticIsKeyword(true);
+ } break;
./
-FunctionBodyOpt: FunctionBody ;
+ClassRBrace: T_RBRACE;
+/. case $rule_number: ./
+ClassStaticQualifier: T_STATIC;
+/.
+ case $rule_number: {
+ lexer->setStaticIsKeyword(false);
+ } break;
+./
-FunctionBody: SourceElements ;
+ClassHeritageOpt: ;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ());
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-Program: Empty ;
+ClassHeritageOpt: T_EXTENDS LeftHandSideExpression;
+/.
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ } break;
+./
-Program: SourceElements ;
+ClassBodyOpt: ;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ());
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-SourceElements: SourceElement ;
+ClassBodyOpt: ClassElementList;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement);
-} break;
+ case $rule_number: {
+ if (sym(1).Node)
+ sym(1).Node = sym(1).ClassElementList->finish();
+ } break;
./
-SourceElements: SourceElements SourceElement ;
+ClassElementList: ClassElement;
+
+ClassElementList: ClassElementList ClassElement;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement);
-} break;
+ case $rule_number: {
+ if (sym(2).Node)
+ sym(1).ClassElementList = sym(1).ClassElementList->append(sym(2).ClassElementList);
+ } break;
./
-SourceElement: Statement ;
+ClassElement: MethodDefinition;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement);
-} break;
+ case $rule_number: {
+ AST::ClassElementList *node = new (pool) AST::ClassElementList(sym(1).PatternProperty, false);
+ sym(1).Node = node;
+ } break;
./
-SourceElement: FunctionDeclaration ;
+ClassElement: ClassStaticQualifier MethodDefinition;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration);
-} break;
+ case $rule_number: {
+ lexer->setStaticIsKeyword(true);
+ AST::ClassElementList *node = new (pool) AST::ClassElementList(sym(2).PatternProperty, true);
+ sym(1).Node = node;
+ } break;
./
-PropertyAssignmentListOpt: ;
+ClassElement: T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
+./
+
+-- Scripts and Modules
+
+Script: ;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-PropertyAssignmentListOpt: PropertyAssignmentList ;
+Script: ScriptBody;
+ScriptBody: StatementList;
/.
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::Program(sym(1).StatementList->finish());
+ } break;
+./
+
+Module: ModuleBodyOpt;
+/. case $rule_number: { UNIMPLEMENTED; } ./
+
+ModuleBody: ModuleItemList;
+
+ModuleBodyOpt: ;
+ModuleBodyOpt: ModuleBody;
+
+ModuleItemList: ModuleItem;
+ModuleItemList: ModuleItemList ModuleItem;
+
+ModuleItem: ImportDeclaration T_AUTOMATIC_SEMICOLON;
+ModuleItem: ImportDeclaration T_SEMICOLON;
+ModuleItem: ExportDeclaration T_AUTOMATIC_SEMICOLON;
+ModuleItem: ExportDeclaration T_SEMICOLON;
+ModuleItem: StatementListItem;
+
+ImportDeclaration: T_IMPORT ImportClause FromClause;
+ImportDeclaration: T_IMPORT ModuleSpecifier;
+
+ImportClause: ImportedDefaultBinding;
+ImportClause: NameSpaceImport;
+ImportClause: NamedImports;
+ImportClause: ImportedDefaultBinding T_COMMA NameSpaceImport;
+ImportClause: ImportedDefaultBinding T_COMMA NamedImports;
+
+ImportedDefaultBinding: ImportedBinding;
+
+NameSpaceImport: T_STAR T_AS ImportedBinding;
+
+NamedImports: T_LBRACE T_RBRACE;
+NamedImports: T_LBRACE ImportsList T_RBRACE;
+NamedImports: T_LBRACE ImportsList T_COMMA T_RBRACE;
+
+FromClause: T_FROM ModuleSpecifier;
+
+ImportsList: ImportSpecifier;
+ImportsList: ImportsList T_COMMA ImportSpecifier;
+
+ImportSpecifier: ImportedBinding;
+ImportSpecifier: IdentifierName T_AS ImportedBinding;
+
+ModuleSpecifier: T_STRING_LITERAL;
+
+ImportedBinding: BindingIdentifier;
+
+ExportDeclarationLookahead: ;
+/:
+#define J_SCRIPT_EXPORTDECLARATIONLOOKAHEAD_RULE $rule_number
+:/
+/.
+ case $rule_number: {
+ int token = lookaheadToken(lexer);
+ if (token == T_FUNCTION || token == T_CLASS)
+ pushToken(T_FORCE_DECLARATION);
+ } break;
+./
+
+ExportDeclaration: T_EXPORT T_STAR FromClause;
+ExportDeclaration: T_EXPORT ExportClause FromClause;
+ExportDeclaration: T_EXPORT ExportClause;
+ExportDeclaration: T_EXPORT VariableStatement;
+ExportDeclaration: T_EXPORT Declaration;
+ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead T_FORCE_DECLARATION HoistableDeclaration_Default;
+ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead T_FORCE_DECLARATION ClassDeclaration_Default;
+ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead AssignmentExpression_In; -- [lookahead ∉ { function, class }]
+
+ExportClause: T_LBRACE T_RBRACE;
+ExportClause: T_LBRACE ExportsList T_RBRACE;
+ExportClause: T_LBRACE ExportsList T_COMMA T_RBRACE;
+
+ExportsList: ExportSpecifier;
+ExportsList: ExportsList T_COMMA ExportSpecifier;
+
+ExportSpecifier: IdentifierName;
+ExportSpecifier: IdentifierName T_AS IdentifierName;
+
+-- Old top level code
+
+/.
+ // ------------ end of switch statement
} // switch
action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT);
} // if
} while (action != 0);
+#ifdef PARSER_DEBUG
+ qDebug() << "Done or error.";
+#endif
+
if (first_token == last_token) {
const int errorState = state_stack[tos];
// automatic insertion of `;'
if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken))
|| t_action(errorState, T_COMPATIBILITY_SEMICOLON))) {
+#ifdef PARSER_DEBUG
+ qDebug() << "Inserting automatic semicolon.";
+#endif
SavedToken &tk = token_buffer[0];
tk.token = yytoken;
tk.dval = yylval;
@@ -3260,8 +4211,7 @@ PropertyAssignmentListOpt: PropertyAssignmentList ;
for (int tk = 1; tk < TERMINAL_COUNT; ++tk) {
if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM ||
- tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION ||
- tk == T_FEED_JS_SOURCE_ELEMENT)
+ tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION)
continue;
int a = t_action(errorState, tk);
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 0d1b512c92..d254279cbf 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -45,6 +45,27 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
+FunctionExpression *asAnonymousFunctionDefinition(Node *n)
+{
+ if (!n)
+ return nullptr;
+ FunctionExpression *f = n->asFunctionDefinition();
+ if (!f || !f->name.isNull())
+ return nullptr;
+ return f;
+}
+
+ClassExpression *asAnonymousClassDefinition(Node *n)
+{
+ if (!n)
+ return nullptr;
+ ClassExpression *c = n->asClassDefinition();
+ if (!c || !c->name.isNull())
+ return nullptr;
+ return c;
+}
+
+
void Node::accept(Visitor *visitor)
{
if (visitor->preVisit(this)) {
@@ -79,11 +100,67 @@ UiObjectMember *Node::uiObjectMemberCast()
return nullptr;
}
+LeftHandSideExpression *Node::leftHandSideExpressionCast()
+{
+ return nullptr;
+}
+
+Pattern *Node::patternCast()
+{
+ return nullptr;
+}
+
+FunctionExpression *Node::asFunctionDefinition()
+{
+ return nullptr;
+}
+
+ClassExpression *Node::asClassDefinition()
+{
+ return nullptr;
+}
+
ExpressionNode *ExpressionNode::expressionCast()
{
return this;
}
+FormalParameterList *ExpressionNode::reparseAsFormalParameterList(MemoryPool *pool)
+{
+ AST::ExpressionNode *expr = this;
+ AST::FormalParameterList *f = nullptr;
+ if (AST::Expression *commaExpr = AST::cast<AST::Expression *>(expr)) {
+ f = commaExpr->left->reparseAsFormalParameterList(pool);
+ if (!f)
+ return nullptr;
+
+ expr = commaExpr->right;
+ }
+
+ AST::ExpressionNode *rhs = nullptr;
+ if (AST::BinaryExpression *assign = AST::cast<AST::BinaryExpression *>(expr)) {
+ if (assign->op != QSOperator::Assign)
+ return nullptr;
+ expr = assign->left;
+ rhs = assign->right;
+ }
+ AST::PatternElement *binding = nullptr;
+ if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
+ binding = new (pool) AST::PatternElement(idExpr->name, rhs);
+ binding->identifierToken = idExpr->identifierToken;
+ } else if (AST::Pattern *p = expr->patternCast()) {
+ SourceLocation loc;
+ QString s;
+ if (!p->convertLiteralToAssignmentPattern(pool, &loc, &s))
+ return nullptr;
+ binding = new (pool) AST::PatternElement(p, rhs);
+ binding->identifierToken = p->firstSourceLocation();
+ }
+ if (!binding)
+ return nullptr;
+ return new (pool) AST::FormalParameterList(f, binding);
+}
+
BinaryExpression *BinaryExpression::binaryExpressionCast()
{
return this;
@@ -107,6 +184,16 @@ void NestedExpression::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+FunctionExpression *NestedExpression::asFunctionDefinition()
+{
+ return expression->asFunctionDefinition();
+}
+
+ClassExpression *NestedExpression::asClassDefinition()
+{
+ return expression->asClassDefinition();
+}
+
void ThisExpression::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -147,7 +234,7 @@ void FalseLiteral::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void StringLiteral::accept0(Visitor *visitor)
+void SuperLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
}
@@ -155,7 +242,8 @@ void StringLiteral::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void NumericLiteral::accept0(Visitor *visitor)
+
+void StringLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
}
@@ -163,81 +251,229 @@ void NumericLiteral::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void RegExpLiteral::accept0(Visitor *visitor)
+void TemplateLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
+ if (next)
+ accept(next, visitor);
}
visitor->endVisit(this);
}
-void ArrayLiteral::accept0(Visitor *visitor)
+void NumericLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(elements, visitor);
- accept(elision, visitor);
}
visitor->endVisit(this);
}
-void ObjectLiteral::accept0(Visitor *visitor)
+void RegExpLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(properties, visitor);
}
visitor->endVisit(this);
}
-void ElementList::accept0(Visitor *visitor)
+void ArrayPattern::accept0(Visitor *visitor)
{
- if (visitor->visit(this)) {
- for (ElementList *it = this; it; it = it->next) {
- accept(it->elision, visitor);
- accept(it->expression, visitor);
- }
- }
+ if (visitor->visit(this))
+ accept(elements, visitor);
visitor->endVisit(this);
}
-void Elision::accept0(Visitor *visitor)
+bool ArrayPattern::isValidArrayLiteral(SourceLocation *errorLocation) const {
+ for (PatternElementList *it = elements; it != nullptr; it = it->next) {
+ PatternElement *e = it->element;
+ if (e && e->bindingTarget != nullptr) {
+ if (errorLocation)
+ *errorLocation = e->firstSourceLocation();
+ return false;
+ }
+ }
+ return true;
+}
+
+void ObjectPattern::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- // ###
+ accept(properties, visitor);
}
visitor->endVisit(this);
}
-void PropertyNameAndValue::accept0(Visitor *visitor)
-{
- if (visitor->visit(this)) {
- accept(name, visitor);
- accept(value, visitor);
+/*
+ This is the grammar for AssignmentPattern that we need to convert the literal to:
+
+ AssignmentPattern:
+ ObjectAssignmentPattern
+ ArrayAssignmentPattern
+ ArrayAssignmentPattern:
+ [ ElisionOpt AssignmentRestElementOpt ]
+ [ AssignmentElementList ]
+ [ AssignmentElementList , ElisionOpt AssignmentRestElementOpt ]
+ AssignmentElementList:
+ AssignmentElisionElement
+ AssignmentElementList , AssignmentElisionElement
+ AssignmentElisionElement:
+ ElisionOpt AssignmentElement
+ AssignmentRestElement:
+ ... DestructuringAssignmentTarget
+
+ ObjectAssignmentPattern:
+ {}
+ { AssignmentPropertyList }
+ { AssignmentPropertyList, }
+ AssignmentPropertyList:
+ AssignmentProperty
+ AssignmentPropertyList , AssignmentProperty
+ AssignmentProperty:
+ IdentifierReference InitializerOpt_In
+ PropertyName:
+ AssignmentElement
+
+ AssignmentElement:
+ DestructuringAssignmentTarget InitializerOpt_In
+ DestructuringAssignmentTarget:
+ LeftHandSideExpression
+
+ It was originally parsed with the following grammar:
+
+ArrayLiteral:
+ [ ElisionOpt ]
+ [ ElementList ]
+ [ ElementList , ElisionOpt ]
+ElementList:
+ ElisionOpt AssignmentExpression_In
+ ElisionOpt SpreadElement
+ ElementList , ElisionOpt AssignmentExpression_In
+ ElementList , Elisionopt SpreadElement
+SpreadElement:
+ ... AssignmentExpression_In
+ObjectLiteral:
+ {}
+ { PropertyDefinitionList }
+ { PropertyDefinitionList , }
+PropertyDefinitionList:
+ PropertyDefinition
+ PropertyDefinitionList , PropertyDefinition
+PropertyDefinition:
+ IdentifierReference
+ CoverInitializedName
+ PropertyName : AssignmentExpression_In
+ MethodDefinition
+PropertyName:
+ LiteralPropertyName
+ ComputedPropertyName
+
+*/
+bool ArrayPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
+{
+ if (parseMode == Binding)
+ return true;
+ for (auto *it = elements; it; it = it->next) {
+ if (!it->element)
+ continue;
+ if (it->element->type == PatternElement::SpreadElement && it->next) {
+ *errorLocation = it->element->firstSourceLocation();
+ *errorMessage = QString::fromLatin1("'...' can only appear as last element in a destructuring list.");
+ return false;
+ }
+ if (!it->element->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage))
+ return false;
}
+ parseMode = Binding;
+ return true;
+}
- visitor->endVisit(this);
+bool ObjectPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
+{
+ if (parseMode == Binding)
+ return true;
+ for (auto *it = properties; it; it = it->next) {
+ if (!it->property->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage))
+ return false;
+ }
+ parseMode = Binding;
+ return true;
}
-void PropertyGetterSetter::accept0(Visitor *visitor)
+bool PatternElement::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
{
- if (visitor->visit(this)) {
- accept(name, visitor);
- accept(formals, visitor);
- accept(functionBody, visitor);
+ Q_ASSERT(type == Literal || type == SpreadElement);
+ Q_ASSERT(bindingIdentifier.isNull());
+ Q_ASSERT(bindingTarget == nullptr);
+ Q_ASSERT(bindingTarget == nullptr);
+ Q_ASSERT(initializer);
+ ExpressionNode *init = initializer;
+
+ initializer = nullptr;
+ LeftHandSideExpression *lhs = init->leftHandSideExpressionCast();
+ if (type == SpreadElement) {
+ if (!lhs) {
+ *errorLocation = init->firstSourceLocation();
+ *errorMessage = QString::fromLatin1("Invalid lhs expression after '...' in destructuring expression.");
+ return false;
+ }
+ } else {
+ type = PatternElement::Binding;
+
+ if (BinaryExpression *b = init->binaryExpressionCast()) {
+ if (b->op != QSOperator::Assign) {
+ *errorLocation = b->operatorToken;
+ *errorMessage = QString::fromLatin1("Invalid assignment operation in destructuring expression");
+ return false;
+ }
+ lhs = b->left->leftHandSideExpressionCast();
+ initializer = b->right;
+ Q_ASSERT(lhs);
+ } else {
+ lhs = init->leftHandSideExpressionCast();
+ }
+ if (!lhs) {
+ *errorLocation = init->firstSourceLocation();
+ *errorMessage = QString::fromLatin1("Destructuring target is not a left hand side expression.");
+ return false;
+ }
}
- visitor->endVisit(this);
+ if (auto *i = cast<IdentifierExpression *>(lhs)) {
+ bindingIdentifier = i->name;
+ identifierToken = i->identifierToken;
+ return true;
+ }
+
+ bindingTarget = lhs;
+ if (auto *p = lhs->patternCast()) {
+ if (!p->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage))
+ return false;
+ }
+ return true;
+}
+
+bool PatternProperty::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
+{
+ Q_ASSERT(type != SpreadElement);
+ if (type == Binding)
+ return true;
+ if (type == Getter || type == Setter) {
+ *errorLocation = firstSourceLocation();
+ *errorMessage = QString::fromLatin1("Invalid getter/setter in destructuring expression.");
+ return false;
+ }
+ Q_ASSERT(type == Literal);
+ return PatternElement::convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage);
}
-void PropertyAssignmentList::accept0(Visitor *visitor)
+
+void Elision::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- for (PropertyAssignmentList *it = this; it; it = it->next) {
- accept(it->assignment, visitor);
- }
+ // ###
}
visitor->endVisit(this);
@@ -518,15 +754,6 @@ void VariableDeclarationList::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void VariableDeclaration::accept0(Visitor *visitor)
-{
- if (visitor->visit(this)) {
- accept(expression, visitor);
- }
-
- visitor->endVisit(this);
-}
-
void EmptyStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -579,17 +806,6 @@ void ForStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(initialiser, visitor);
- accept(condition, visitor);
- accept(expression, visitor);
- accept(statement, visitor);
- }
-
- visitor->endVisit(this);
-}
-
-void LocalForStatement::accept0(Visitor *visitor)
-{
- if (visitor->visit(this)) {
accept(declarations, visitor);
accept(condition, visitor);
accept(expression, visitor);
@@ -602,7 +818,7 @@ void LocalForStatement::accept0(Visitor *visitor)
void ForEachStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(initialiser, visitor);
+ accept(lhs, visitor);
accept(expression, visitor);
accept(statement, visitor);
}
@@ -610,18 +826,15 @@ void ForEachStatement::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void LocalForEachStatement::accept0(Visitor *visitor)
+void ContinueStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(declaration, visitor);
- accept(expression, visitor);
- accept(statement, visitor);
}
visitor->endVisit(this);
}
-void ContinueStatement::accept0(Visitor *visitor)
+void BreakStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
}
@@ -629,15 +842,16 @@ void ContinueStatement::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void BreakStatement::accept0(Visitor *visitor)
+void ReturnStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
+ accept(expression, visitor);
}
visitor->endVisit(this);
}
-void ReturnStatement::accept0(Visitor *visitor)
+void YieldExpression::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(expression, visitor);
@@ -646,6 +860,7 @@ void ReturnStatement::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+
void WithStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -739,6 +954,7 @@ void TryStatement::accept0(Visitor *visitor)
void Catch::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
+ accept(patternElement, visitor);
accept(statement, visitor);
}
@@ -774,57 +990,69 @@ void FunctionExpression::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void FormalParameterList::accept0(Visitor *visitor)
+FunctionExpression *FunctionExpression::asFunctionDefinition()
{
- if (visitor->visit(this)) {
- // ###
- }
-
- visitor->endVisit(this);
+ return this;
}
-void FunctionBody::accept0(Visitor *visitor)
+QStringList FormalParameterList::formals() const
{
- if (visitor->visit(this)) {
- accept(elements, visitor);
+ QStringList formals;
+ int i = 0;
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ if (it->element) {
+ QString name = it->element->bindingIdentifier.toString();
+ int duplicateIndex = formals.indexOf(name);
+ if (duplicateIndex >= 0) {
+ // change the name of the earlier argument to enforce the lookup semantics from the spec
+ formals[duplicateIndex] += QLatin1String("#") + QString::number(i);
+ }
+ formals += name;
+ }
+ ++i;
}
-
- visitor->endVisit(this);
+ return formals;
}
-void Program::accept0(Visitor *visitor)
+QStringList FormalParameterList::boundNames() const
{
- if (visitor->visit(this)) {
- accept(elements, visitor);
+ QStringList names;
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ if (it->element)
+ it->element->boundNames(&names);
}
-
- visitor->endVisit(this);
+ return names;
}
-void SourceElements::accept0(Visitor *visitor)
+void FormalParameterList::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- for (SourceElements *it = this; it; it = it->next) {
- accept(it->element, visitor);
- }
+ accept(element, visitor);
+ if (next)
+ accept(next, visitor);
}
visitor->endVisit(this);
}
-void FunctionSourceElement::accept0(Visitor *visitor)
+FormalParameterList *FormalParameterList::finish(QQmlJS::MemoryPool *pool)
{
- if (visitor->visit(this)) {
- accept(declaration, visitor);
- }
+ FormalParameterList *front = next;
+ next = nullptr;
- visitor->endVisit(this);
+ int i = 0;
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ if (it->element && it->element->bindingIdentifier.isEmpty())
+ it->element->bindingIdentifier = pool->newString(QLatin1String("arg#") + QString::number(i));
+ ++i;
+ }
+ return front;
}
-void StatementSourceElement::accept0(Visitor *visitor)
+void Program::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(statement, visitor);
+ accept(statements, visitor);
}
visitor->endVisit(this);
@@ -1006,6 +1234,153 @@ void UiEnumMemberList::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+void TaggedTemplate::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ accept(templateLiteral, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternElement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(bindingTarget, visitor);
+ accept(initializer, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternElement::boundNames(QStringList *names)
+{
+ if (bindingTarget) {
+ if (PatternElementList *e = elementList())
+ e->boundNames(names);
+ else if (PatternPropertyList *p = propertyList())
+ p->boundNames(names);
+ } else {
+ names->append(bindingIdentifier.toString());
+ }
+}
+
+void PatternElementList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(elision, visitor);
+ accept(element, visitor);
+ if (next)
+ accept(next, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternElementList::boundNames(QStringList *names)
+{
+ for (PatternElementList *it = this; it; it = it->next) {
+ if (it->element)
+ it->element->boundNames(names);
+ }
+}
+
+void PatternProperty::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(name, visitor);
+ accept(bindingTarget, visitor);
+ accept(initializer, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternProperty::boundNames(QStringList *names)
+{
+ PatternElement::boundNames(names);
+}
+
+void PatternPropertyList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(property, visitor);
+ if (next)
+ accept(next, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternPropertyList::boundNames(QStringList *names)
+{
+ for (PatternPropertyList *it = this; it; it = it->next)
+ it->property->boundNames(names);
+}
+
+void ComputedPropertyName::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ClassExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(heritage, visitor);
+ accept(elements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+ClassExpression *ClassExpression::asClassDefinition()
+{
+ return this;
+}
+
+void ClassDeclaration::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(heritage, visitor);
+ accept(elements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ClassElementList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(property, visitor);
+ if (next)
+ accept(next, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+ClassElementList *ClassElementList::finish()
+{
+ ClassElementList *front = next;
+ next = nullptr;
+ return front;
+}
+
+Pattern *Pattern::patternCast()
+{
+ return this;
+}
+
+LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast()
+{
+ return this;
+}
+
} } // namespace QQmlJS::AST
QT_QML_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 9c5fd5adf6..07999404b4 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -77,6 +77,8 @@ enum Op {
Div,
InplaceDiv,
Equal,
+ Exp,
+ InplaceExp,
Ge,
Gt,
In,
@@ -110,6 +112,13 @@ namespace QQmlJS {
namespace AST {
+enum class VariableScope {
+ NoScope,
+ Var,
+ Let,
+ Const
+};
+
template <typename T1, typename T2>
T1 cast(T2 *ast)
{
@@ -119,6 +128,9 @@ T1 cast(T2 *ast)
return 0;
}
+FunctionExpression *asAnonymousFunctionDefinition(AST::Node *n);
+ClassExpression *asAnonymousClassDefinition(AST::Node *n);
+
class QML_PARSER_EXPORT Node: public Managed
{
public:
@@ -126,7 +138,7 @@ public:
Kind_Undefined,
Kind_ArgumentList,
- Kind_ArrayLiteral,
+ Kind_ArrayPattern,
Kind_ArrayMemberExpression,
Kind_BinaryExpression,
Kind_Block,
@@ -148,6 +160,7 @@ public:
Kind_Expression,
Kind_ExpressionStatement,
Kind_FalseLiteral,
+ Kind_SuperLiteral,
Kind_FieldMemberExpression,
Kind_Finally,
Kind_ForEachStatement,
@@ -156,38 +169,38 @@ public:
Kind_FunctionBody,
Kind_FunctionDeclaration,
Kind_FunctionExpression,
- Kind_FunctionSourceElement,
+ Kind_ClassExpression,
+ Kind_ClassDeclaration,
Kind_IdentifierExpression,
Kind_IdentifierPropertyName,
+ Kind_ComputedPropertyName,
Kind_IfStatement,
Kind_LabelledStatement,
- Kind_LocalForEachStatement,
- Kind_LocalForStatement,
Kind_NewExpression,
Kind_NewMemberExpression,
Kind_NotExpression,
Kind_NullExpression,
+ Kind_YieldExpression,
Kind_NumericLiteral,
Kind_NumericLiteralPropertyName,
- Kind_ObjectLiteral,
+ Kind_ObjectPattern,
Kind_PostDecrementExpression,
Kind_PostIncrementExpression,
Kind_PreDecrementExpression,
Kind_PreIncrementExpression,
Kind_Program,
- Kind_PropertyAssignmentList,
+ Kind_PropertyDefinitionList,
Kind_PropertyGetterSetter,
Kind_PropertyName,
Kind_PropertyNameAndValue,
Kind_RegExpLiteral,
Kind_ReturnStatement,
- Kind_SourceElement,
- Kind_SourceElements,
Kind_StatementList,
- Kind_StatementSourceElement,
Kind_StringLiteral,
Kind_StringLiteralPropertyName,
Kind_SwitchStatement,
+ Kind_TemplateLiteral,
+ Kind_TaggedTemplate,
Kind_ThisExpression,
Kind_ThrowStatement,
Kind_TildeExpression,
@@ -203,6 +216,12 @@ public:
Kind_WhileStatement,
Kind_WithStatement,
Kind_NestedExpression,
+ Kind_ClassElementList,
+ Kind_PatternElement,
+ Kind_PatternElementList,
+ Kind_PatternProperty,
+ Kind_PatternPropertyList,
+
Kind_UiArrayBinding,
Kind_UiImport,
@@ -235,6 +254,11 @@ public:
virtual BinaryExpression *binaryExpressionCast();
virtual Statement *statementCast();
virtual UiObjectMember *uiObjectMemberCast();
+ virtual LeftHandSideExpression *leftHandSideExpressionCast();
+ virtual Pattern *patternCast();
+ // implements the IsFunctionDefinition rules in the spec
+ virtual FunctionExpression *asFunctionDefinition();
+ virtual ClassExpression *asClassDefinition();
void accept(Visitor *visitor);
static void accept(Node *node, Visitor *visitor);
@@ -256,6 +280,14 @@ public:
ExpressionNode() {}
ExpressionNode *expressionCast() override;
+
+ AST::FormalParameterList *reparseAsFormalParameterList(MemoryPool *pool);
+
+};
+
+class QML_PARSER_EXPORT LeftHandSideExpression : public ExpressionNode
+{
+ LeftHandSideExpression *leftHandSideExpressionCast() override;
};
class QML_PARSER_EXPORT Statement: public Node
@@ -266,7 +298,7 @@ public:
Statement *statementCast() override;
};
-class QML_PARSER_EXPORT NestedExpression: public ExpressionNode
+class QML_PARSER_EXPORT NestedExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NestedExpression)
@@ -283,13 +315,17 @@ public:
SourceLocation lastSourceLocation() const override
{ return rparenToken; }
+ FunctionExpression *asFunctionDefinition() override;
+ ClassExpression *asClassDefinition() override;
+
+
// attributes
ExpressionNode *expression;
SourceLocation lparenToken;
SourceLocation rparenToken;
};
-class QML_PARSER_EXPORT ThisExpression: public ExpressionNode
+class QML_PARSER_EXPORT ThisExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(ThisExpression)
@@ -308,7 +344,7 @@ public:
SourceLocation thisToken;
};
-class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode
+class QML_PARSER_EXPORT IdentifierExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(IdentifierExpression)
@@ -329,7 +365,7 @@ public:
SourceLocation identifierToken;
};
-class QML_PARSER_EXPORT NullExpression: public ExpressionNode
+class QML_PARSER_EXPORT NullExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NullExpression)
@@ -348,7 +384,7 @@ public:
SourceLocation nullToken;
};
-class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode
+class QML_PARSER_EXPORT TrueLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(TrueLiteral)
@@ -367,7 +403,7 @@ public:
SourceLocation trueToken;
};
-class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode
+class QML_PARSER_EXPORT FalseLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(FalseLiteral)
@@ -386,7 +422,27 @@ public:
SourceLocation falseToken;
};
-class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode
+class QML_PARSER_EXPORT SuperLiteral : public LeftHandSideExpression
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(SuperLiteral)
+
+ SuperLiteral() { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return superToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return superToken; }
+
+// attributes
+ SourceLocation superToken;
+};
+
+
+class QML_PARSER_EXPORT NumericLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NumericLiteral)
@@ -407,7 +463,7 @@ public:
SourceLocation literalToken;
};
-class QML_PARSER_EXPORT StringLiteral: public ExpressionNode
+class QML_PARSER_EXPORT StringLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(StringLiteral)
@@ -428,7 +484,30 @@ public:
SourceLocation literalToken;
};
-class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode
+class QML_PARSER_EXPORT TemplateLiteral : public LeftHandSideExpression
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TemplateLiteral)
+
+ TemplateLiteral(const QStringRef &str, ExpressionNode *e)
+ : value(str), expression(e), next(nullptr)
+ { kind = K; }
+
+ SourceLocation firstSourceLocation() const override
+ { return literalToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : (expression ? expression->lastSourceLocation() : literalToken); }
+
+ void accept0(Visitor *visitor) override;
+
+ QStringRef value;
+ ExpressionNode *expression;
+ TemplateLiteral *next;
+ SourceLocation literalToken;
+};
+
+class QML_PARSER_EXPORT RegExpLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(RegExpLiteral)
@@ -450,22 +529,26 @@ public:
SourceLocation literalToken;
};
-class QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode
+class QML_PARSER_EXPORT Pattern : public LeftHandSideExpression
{
public:
- QQMLJS_DECLARE_AST_NODE(ArrayLiteral)
-
- ArrayLiteral(Elision *e):
- elements (nullptr), elision (e)
- { kind = K; }
+ enum ParseMode {
+ Literal,
+ Binding
+ };
+ Pattern *patternCast() override;
+ virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) = 0;
+ ParseMode parseMode = Literal;
+};
- ArrayLiteral(ElementList *elts):
- elements (elts), elision (nullptr)
- { kind = K; }
+class QML_PARSER_EXPORT ArrayPattern : public Pattern
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ArrayPattern)
- ArrayLiteral(ElementList *elts, Elision *e):
- elements (elts), elision (e)
- { kind = K; }
+ ArrayPattern(PatternElementList *elts)
+ : elements(elts)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -475,24 +558,28 @@ public:
SourceLocation lastSourceLocation() const override
{ return rbracketToken; }
+ bool isValidArrayLiteral(SourceLocation *errorLocation = nullptr) const;
+
+ bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override;
+
// attributes
- ElementList *elements;
- Elision *elision;
+ PatternElementList *elements = nullptr;
SourceLocation lbracketToken;
SourceLocation commaToken;
SourceLocation rbracketToken;
};
-class QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode
+class QML_PARSER_EXPORT ObjectPattern : public Pattern
{
public:
- QQMLJS_DECLARE_AST_NODE(ObjectLiteral)
+ QQMLJS_DECLARE_AST_NODE(ObjectPattern)
- ObjectLiteral()
+ ObjectPattern()
{ kind = K; }
- ObjectLiteral(PropertyAssignmentList *plist):
- properties (plist) { kind = K; }
+ ObjectPattern(PatternPropertyList *plist)
+ : properties(plist)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -502,8 +589,10 @@ public:
SourceLocation lastSourceLocation() const override
{ return rbraceToken; }
+ bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override;
+
// attributes
- PropertyAssignmentList *properties = nullptr;
+ PatternPropertyList *properties = nullptr;
SourceLocation lbraceToken;
SourceLocation rbraceToken;
};
@@ -543,53 +632,6 @@ public:
SourceLocation commaToken;
};
-class QML_PARSER_EXPORT ElementList: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(ElementList)
-
- ElementList(Elision *e, ExpressionNode *expr):
- elision (e), expression (expr), next (this)
- { kind = K; }
-
- ElementList(ElementList *previous, Elision *e, ExpressionNode *expr):
- elision (e), expression (expr)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
- }
-
- inline ElementList *finish ()
- {
- ElementList *front = next;
- next = nullptr;
- return front;
- }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- {
- if (elision)
- return elision->firstSourceLocation();
- return expression->firstSourceLocation();
- }
-
- SourceLocation lastSourceLocation() const override
- {
- if (next)
- return next->lastSourceLocation();
- return expression->lastSourceLocation();
- }
-
-// attributes
- Elision *elision;
- ExpressionNode *expression;
- ElementList *next;
- SourceLocation commaToken;
-};
-
class QML_PARSER_EXPORT PropertyName: public Node
{
public:
@@ -609,116 +651,184 @@ public:
SourceLocation propertyNameToken;
};
-class QML_PARSER_EXPORT PropertyAssignment: public Node
+class QML_PARSER_EXPORT PatternElement : public Node
{
public:
- PropertyAssignment(PropertyName *n)
- : name(n)
- {}
+ QQMLJS_DECLARE_AST_NODE(PatternElement)
+
+ enum Type {
+ // object literal types
+ Literal,
+ Getter,
+ Setter,
+
+ // used by both bindings and literals
+ SpreadElement,
+ RestElement = SpreadElement,
+
+ // binding types
+ Binding,
+ };
+
+ PatternElement(ExpressionNode *i = nullptr, Type t = Literal)
+ : initializer(i), type(t)
+ { kind = K; }
+
+ PatternElement(const QStringRef &n, ExpressionNode *i = nullptr, Type t = Binding)
+ : bindingIdentifier(n), initializer(i), type(t)
+ {
+ Q_ASSERT(t >= RestElement);
+ kind = K;
+ }
+
+ PatternElement(Pattern *pattern, ExpressionNode *i = nullptr, Type t = Binding)
+ : bindingTarget(pattern), initializer(i), type(t)
+ {
+ Q_ASSERT(t >= RestElement);
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+ virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage);
+
+ SourceLocation firstSourceLocation() const override
+ { return identifierToken.isValid() ? identifierToken : (bindingTarget ? bindingTarget->firstSourceLocation() : initializer->firstSourceLocation()); }
+
+ SourceLocation lastSourceLocation() const override
+ { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : identifierToken); }
+
+ ExpressionNode *destructuringTarget() const { return bindingTarget; }
+ Pattern *destructuringPattern() const { return bindingTarget ? bindingTarget->patternCast() : nullptr; }
+ PatternElementList *elementList() const { ArrayPattern *a = cast<ArrayPattern *>(bindingTarget); return a ? a->elements : nullptr; }
+ PatternPropertyList *propertyList() const { ObjectPattern *o = cast<ObjectPattern *>(bindingTarget); return o ? o->properties : nullptr; }
+
+ bool isVariableDeclaration() const { return scope != VariableScope::NoScope; }
+ bool isLexicallyScoped() const { return scope == VariableScope::Let || scope == VariableScope::Const; }
+
+ virtual void boundNames(QStringList *names);
+
// attributes
- PropertyName *name;
+ SourceLocation identifierToken;
+ QStringRef bindingIdentifier;
+ ExpressionNode *bindingTarget = nullptr;
+ ExpressionNode *initializer = nullptr;
+ Type type = Literal;
+ // when used in a VariableDeclarationList
+ VariableScope scope = VariableScope::NoScope;
};
-class QML_PARSER_EXPORT PropertyAssignmentList: public Node
+class QML_PARSER_EXPORT PatternElementList : public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(PropertyAssignmentList)
+ QQMLJS_DECLARE_AST_NODE(PatternElementList)
- PropertyAssignmentList(PropertyAssignment *assignment)
- : assignment(assignment)
- , next(this)
+ PatternElementList(Elision *elision, PatternElement *element)
+ : elision(elision), element(element), next(this)
{ kind = K; }
- PropertyAssignmentList(PropertyAssignmentList *previous, PropertyAssignment *assignment)
- : assignment(assignment)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
+ PatternElementList *append(PatternElementList *n) {
+ n->next = next;
+ next = n;
+ return n;
}
- inline PropertyAssignmentList *finish ()
+ inline PatternElementList *finish ()
{
- PropertyAssignmentList *front = next;
- next = nullptr;
+ PatternElementList *front = next;
+ next = 0;
return front;
}
void accept0(Visitor *visitor) override;
+ void boundNames(QStringList *names);
+
SourceLocation firstSourceLocation() const override
- { return assignment->firstSourceLocation(); }
+ { return elision ? elision->firstSourceLocation() : element->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : assignment->lastSourceLocation(); }
+ { return next ? next->lastSourceLocation() : (element ? element->lastSourceLocation() : elision->lastSourceLocation()); }
-// attributes
- PropertyAssignment *assignment;
- PropertyAssignmentList *next;
- SourceLocation commaToken;
+ Elision *elision = nullptr;
+ PatternElement *element = nullptr;
+ PatternElementList *next;
};
-class QML_PARSER_EXPORT PropertyNameAndValue: public PropertyAssignment
+class QML_PARSER_EXPORT PatternProperty : public PatternElement
{
public:
- QQMLJS_DECLARE_AST_NODE(PropertyNameAndValue)
+ QQMLJS_DECLARE_AST_NODE(PatternProperty)
+
+ PatternProperty(PropertyName *name, ExpressionNode *i = nullptr, Type t = Literal)
+ : PatternElement(i, t), name(name)
+ { kind = K; }
- PropertyNameAndValue(PropertyName *n, ExpressionNode *v)
- : PropertyAssignment(n), value(v)
+ PatternProperty(PropertyName *name, const QStringRef &n, ExpressionNode *i = nullptr)
+ : PatternElement(n, i), name(name)
+ { kind = K; }
+
+ PatternProperty(PropertyName *name, Pattern *pattern, ExpressionNode *i = nullptr)
+ : PatternElement(pattern, i), name(name)
{ kind = K; }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
{ return name->firstSourceLocation(); }
-
SourceLocation lastSourceLocation() const override
- { return value->lastSourceLocation(); }
+ {
+ SourceLocation loc = PatternElement::lastSourceLocation();
+ return loc.isValid() ? loc : name->lastSourceLocation();
+ }
+
+ void boundNames(QStringList *names) override;
+ bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override;
// attributes
+ PropertyName *name;
SourceLocation colonToken;
- ExpressionNode *value;
- SourceLocation commaToken;
};
-class QML_PARSER_EXPORT PropertyGetterSetter: public PropertyAssignment
+
+class QML_PARSER_EXPORT PatternPropertyList : public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(PropertyGetterSetter)
-
- enum Type {
- Getter,
- Setter
- };
+ QQMLJS_DECLARE_AST_NODE(PatternPropertyList)
- PropertyGetterSetter(PropertyName *n, FunctionBody *b)
- : PropertyAssignment(n), type(Getter), formals(nullptr), functionBody (b)
+ PatternPropertyList(PatternProperty *property)
+ : property(property), next(this)
{ kind = K; }
- PropertyGetterSetter(PropertyName *n, FormalParameterList *f, FunctionBody *b)
- : PropertyAssignment(n), type(Setter), formals(f), functionBody (b)
- { kind = K; }
+ PatternPropertyList(PatternPropertyList *previous, PatternProperty *property)
+ : property(property), next(this)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
void accept0(Visitor *visitor) override;
+ void boundNames(QStringList *names);
+
+ inline PatternPropertyList *finish ()
+ {
+ PatternPropertyList *front = next;
+ next = 0;
+ return front;
+ }
+
SourceLocation firstSourceLocation() const override
- { return getSetToken; }
+ { return property->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return rbraceToken; }
+ { return next ? next->lastSourceLocation() : property->lastSourceLocation(); }
-// attributes
- Type type;
- SourceLocation getSetToken;
- SourceLocation lparenToken;
- FormalParameterList *formals;
- SourceLocation rparenToken;
- SourceLocation lbraceToken;
- FunctionBody *functionBody;
- SourceLocation rbraceToken;
+ PatternProperty *property;
+ PatternPropertyList *next;
};
-class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName
+class QML_PARSER_EXPORT IdentifierPropertyName : public PropertyName
{
public:
QQMLJS_DECLARE_AST_NODE(IdentifierPropertyName)
@@ -766,7 +876,31 @@ public:
double id;
};
-class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode
+class QML_PARSER_EXPORT ComputedPropertyName : public PropertyName
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ComputedPropertyName)
+
+ ComputedPropertyName(ExpressionNode *expression)
+ : expression(expression)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ QString asString() const override { return QString(); }
+
+ SourceLocation firstSourceLocation() const override
+ { return expression->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+};
+
+
+class QML_PARSER_EXPORT ArrayMemberExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(ArrayMemberExpression)
@@ -790,7 +924,7 @@ public:
SourceLocation rbracketToken;
};
-class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode
+class QML_PARSER_EXPORT FieldMemberExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(FieldMemberExpression)
@@ -814,7 +948,29 @@ public:
SourceLocation identifierToken;
};
-class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode
+class QML_PARSER_EXPORT TaggedTemplate : public LeftHandSideExpression
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TaggedTemplate)
+
+ TaggedTemplate(ExpressionNode *b, TemplateLiteral *t)
+ : base (b), templateLiteral(t)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return base->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return templateLiteral->lastSourceLocation(); }
+
+ // attributes
+ ExpressionNode *base;
+ TemplateLiteral *templateLiteral;
+};
+
+class QML_PARSER_EXPORT NewMemberExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NewMemberExpression)
@@ -839,7 +995,7 @@ public:
SourceLocation rparenToken;
};
-class QML_PARSER_EXPORT NewExpression: public ExpressionNode
+class QML_PARSER_EXPORT NewExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NewExpression)
@@ -860,7 +1016,7 @@ public:
SourceLocation newToken;
};
-class QML_PARSER_EXPORT CallExpression: public ExpressionNode
+class QML_PARSER_EXPORT CallExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(CallExpression)
@@ -924,6 +1080,7 @@ public:
ExpressionNode *expression;
ArgumentList *next;
SourceLocation commaToken;
+ bool isSpreadElement = false;
};
class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode
@@ -1257,16 +1414,15 @@ class QML_PARSER_EXPORT StatementList: public Node
public:
QQMLJS_DECLARE_AST_NODE(StatementList)
- StatementList(Statement *stmt):
- statement (stmt), next (this)
- { kind = K; }
+ // ### This should be a Statement, but FunctionDeclaration currently doesn't inherit it.
+ StatementList(Node *stmt)
+ : statement(stmt), next (this)
+ { kind = K; }
- StatementList(StatementList *previous, Statement *stmt):
- statement (stmt)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
+ StatementList *append(StatementList *n) {
+ n->next = next;
+ next = n;
+ return n;
}
void accept0(Visitor *visitor) override;
@@ -1285,76 +1441,21 @@ public:
}
// attributes
- Statement *statement;
+ Node *statement = nullptr;
StatementList *next;
};
-class QML_PARSER_EXPORT VariableStatement: public Statement
-{
-public:
- QQMLJS_DECLARE_AST_NODE(VariableStatement)
-
- VariableStatement(VariableDeclarationList *vlist):
- declarations (vlist)
- { kind = K; }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return declarationKindToken; }
-
- SourceLocation lastSourceLocation() const override
- { return semicolonToken; }
-
-// attributes
- VariableDeclarationList *declarations;
- SourceLocation declarationKindToken;
- SourceLocation semicolonToken;
-};
-
-class QML_PARSER_EXPORT VariableDeclaration: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(VariableDeclaration)
-
- enum VariableScope {
- FunctionScope,
- BlockScope, // let
- ReadOnlyBlockScope // const
- };
-
- VariableDeclaration(const QStringRef &n, ExpressionNode *e, VariableScope s):
- name (n), expression (e), scope(s)
- { kind = K; }
-
- bool isLexicallyScoped() const { return scope != FunctionScope; }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return identifierToken; }
-
- SourceLocation lastSourceLocation() const override
- { return expression ? expression->lastSourceLocation() : identifierToken; }
-
-// attributes
- QStringRef name;
- ExpressionNode *expression;
- SourceLocation identifierToken;
- VariableScope scope;
-};
-
class QML_PARSER_EXPORT VariableDeclarationList: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(VariableDeclarationList)
- VariableDeclarationList(VariableDeclaration *decl):
- declaration (decl), next (this)
- { kind = K; }
+ VariableDeclarationList(PatternElement *decl)
+ : declaration(decl), next(this)
+ { kind = K; }
- VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl):
- declaration (decl)
+ VariableDeclarationList(VariableDeclarationList *previous, PatternElement *decl)
+ : declaration(decl)
{
kind = K;
next = previous->next;
@@ -1373,7 +1474,7 @@ public:
return declaration->lastSourceLocation();
}
- inline VariableDeclarationList *finish(VariableDeclaration::VariableScope s)
+ inline VariableDeclarationList *finish(VariableScope s)
{
VariableDeclarationList *front = next;
next = nullptr;
@@ -1385,11 +1486,33 @@ public:
}
// attributes
- VariableDeclaration *declaration;
+ PatternElement *declaration;
VariableDeclarationList *next;
SourceLocation commaToken;
};
+class QML_PARSER_EXPORT VariableStatement: public Statement
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(VariableStatement)
+
+ VariableStatement(VariableDeclarationList *vlist):
+ declarations (vlist)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return declarationKindToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return declarations->lastSourceLocation(); }
+
+// attributes
+ VariableDeclarationList *declarations;
+ SourceLocation declarationKindToken;
+};
+
class QML_PARSER_EXPORT EmptyStatement: public Statement
{
public:
@@ -1423,7 +1546,7 @@ public:
{ return expression->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return semicolonToken; }
+ { return expression->lastSourceLocation(); }
// attributes
ExpressionNode *expression;
@@ -1523,35 +1646,11 @@ public:
initialiser (i), condition (c), expression (e), statement (stmt)
{ kind = K; }
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return forToken; }
-
- SourceLocation lastSourceLocation() const override
- { return statement->lastSourceLocation(); }
-
-// attributes
- ExpressionNode *initialiser;
- ExpressionNode *condition;
- ExpressionNode *expression;
- Statement *statement;
- SourceLocation forToken;
- SourceLocation lparenToken;
- SourceLocation firstSemicolonToken;
- SourceLocation secondSemicolonToken;
- SourceLocation rparenToken;
-};
-
-class QML_PARSER_EXPORT LocalForStatement: public Statement
-{
-public:
- QQMLJS_DECLARE_AST_NODE(LocalForStatement)
-
- LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt):
+ ForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt):
declarations (vlist), condition (c), expression (e), statement (stmt)
{ kind = K; }
+
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
@@ -1561,26 +1660,34 @@ public:
{ return statement->lastSourceLocation(); }
// attributes
- VariableDeclarationList *declarations;
+ ExpressionNode *initialiser = nullptr;
+ VariableDeclarationList *declarations = nullptr;
ExpressionNode *condition;
ExpressionNode *expression;
Statement *statement;
SourceLocation forToken;
SourceLocation lparenToken;
- SourceLocation varToken;
SourceLocation firstSemicolonToken;
SourceLocation secondSemicolonToken;
SourceLocation rparenToken;
};
+enum class ForEachType {
+ In,
+ Of
+};
+
class QML_PARSER_EXPORT ForEachStatement: public Statement
{
public:
QQMLJS_DECLARE_AST_NODE(ForEachStatement)
- ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt):
- initialiser (i), expression (e), statement (stmt)
- { kind = K; }
+ ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt)
+ : lhs(i), expression(e), statement(stmt)
+ { kind = K; }
+ ForEachStatement(PatternElement *v, ExpressionNode *e, Statement *stmt)
+ : lhs(v), expression(e), statement(stmt)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -1590,42 +1697,19 @@ public:
SourceLocation lastSourceLocation() const override
{ return statement->lastSourceLocation(); }
-// attributes
- ExpressionNode *initialiser;
- ExpressionNode *expression;
- Statement *statement;
- SourceLocation forToken;
- SourceLocation lparenToken;
- SourceLocation inToken;
- SourceLocation rparenToken;
-};
-
-class QML_PARSER_EXPORT LocalForEachStatement: public Statement
-{
-public:
- QQMLJS_DECLARE_AST_NODE(LocalForEachStatement)
-
- LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt):
- declaration (v), expression (e), statement (stmt)
- { kind = K; }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return forToken; }
-
- SourceLocation lastSourceLocation() const override
- { return statement->lastSourceLocation(); }
+ PatternElement *declaration() const {
+ return AST::cast<PatternElement *>(lhs);
+ }
// attributes
- VariableDeclaration *declaration;
+ Node *lhs;
ExpressionNode *expression;
Statement *statement;
SourceLocation forToken;
SourceLocation lparenToken;
- SourceLocation varToken;
- SourceLocation inToken;
+ SourceLocation inOfToken;
SourceLocation rparenToken;
+ ForEachType type;
};
class QML_PARSER_EXPORT ContinueStatement: public Statement
@@ -1696,6 +1780,28 @@ public:
SourceLocation semicolonToken;
};
+class QML_PARSER_EXPORT YieldExpression: public ExpressionNode
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(YieldExpression)
+
+ YieldExpression(ExpressionNode *e = nullptr):
+ expression (e) { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return yieldToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return expression ? expression->lastSourceLocation() : yieldToken; }
+
+// attributes
+ ExpressionNode *expression;
+ bool isYieldStar = false;
+ SourceLocation yieldToken;
+};
+
class QML_PARSER_EXPORT WithStatement: public Statement
{
public:
@@ -1906,9 +2012,9 @@ class QML_PARSER_EXPORT Catch: public Node
public:
QQMLJS_DECLARE_AST_NODE(Catch)
- Catch(const QStringRef &n, Block *stmt):
- name (n), statement (stmt)
- { kind = K; }
+ Catch(PatternElement *p, Block *stmt)
+ : patternElement(p), statement(stmt)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -1919,7 +2025,7 @@ public:
{ return statement->lastSourceLocation(); }
// attributes
- QStringRef name;
+ PatternElement *patternElement;
Block *statement;
SourceLocation catchToken;
SourceLocation lparenToken;
@@ -1993,7 +2099,7 @@ class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode
public:
QQMLJS_DECLARE_AST_NODE(FunctionExpression)
- FunctionExpression(const QStringRef &n, FormalParameterList *f, FunctionBody *b):
+ FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b):
name (n), formals (f), body (b)
{ kind = K; }
@@ -2005,10 +2111,14 @@ public:
SourceLocation lastSourceLocation() const override
{ return rbraceToken; }
+ FunctionExpression *asFunctionDefinition() override;
+
// attributes
QStringRef name;
+ bool isArrowFunction = false;
+ bool isGenerator = false;
FormalParameterList *formals;
- FunctionBody *body;
+ StatementList *body;
SourceLocation functionToken;
SourceLocation identifierToken;
SourceLocation lparenToken;
@@ -2022,7 +2132,7 @@ class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression
public:
QQMLJS_DECLARE_AST_NODE(FunctionDeclaration)
- FunctionDeclaration(const QStringRef &n, FormalParameterList *f, FunctionBody *b):
+ FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b):
FunctionExpression(n, f, b)
{ kind = K; }
@@ -2034,65 +2144,68 @@ class QML_PARSER_EXPORT FormalParameterList: public Node
public:
QQMLJS_DECLARE_AST_NODE(FormalParameterList)
- FormalParameterList(const QStringRef &n):
- name (n), next (this)
- { kind = K; }
-
- FormalParameterList(FormalParameterList *previous, const QStringRef &n):
- name (n)
+ FormalParameterList(FormalParameterList *previous, PatternElement *e)
+ : element(e)
{
kind = K;
- next = previous->next;
- previous->next = this;
+ if (previous) {
+ next = previous->next;
+ previous->next = this;
+ } else {
+ next = this;
+ }
}
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return identifierToken; }
-
- SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : identifierToken; }
+ FormalParameterList *append(FormalParameterList *n) {
+ n->next = next;
+ next = n;
+ return n;
+ }
- inline FormalParameterList *finish ()
+ bool isSimpleParameterList()
{
- FormalParameterList *front = next;
- next = nullptr;
- return front;
+ AST::FormalParameterList *formals = this;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (e && e->type == PatternElement::RestElement)
+ return false;
+ if (e && (e->initializer || e->bindingTarget))
+ return false;
+ formals = formals->next;
+ }
+ return true;
}
-// attributes
- QStringRef name;
- FormalParameterList *next;
- SourceLocation commaToken;
- SourceLocation identifierToken;
-};
-
-class QML_PARSER_EXPORT SourceElement: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(SourceElement)
-
- inline SourceElement()
- { kind = K; }
-};
+ int length()
+ {
+ // the length property of Function objects
+ int l = 0;
+ AST::FormalParameterList *formals = this;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (!e || e->initializer)
+ break;
+ if (e->type == PatternElement::RestElement)
+ break;
+ ++l;
+ formals = formals->next;
+ }
+ return l;
+ }
-class QML_PARSER_EXPORT SourceElements: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(SourceElements)
+ bool containsName(const QString &name) const {
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ PatternElement *b = it->element;
+ // ### handle binding patterns
+ if (b && b->bindingIdentifier == name)
+ return true;
+ }
+ return false;
+ }
- SourceElements(SourceElement *elt):
- element (elt), next (this)
- { kind = K; }
+ QStringList formals() const;
- SourceElements(SourceElements *previous, SourceElement *elt):
- element (elt)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
- }
+ QStringList boundNames() const;
void accept0(Visitor *visitor) override;
@@ -2102,100 +2215,111 @@ public:
SourceLocation lastSourceLocation() const override
{ return next ? next->lastSourceLocation() : element->lastSourceLocation(); }
- inline SourceElements *finish ()
- {
- SourceElements *front = next;
- next = nullptr;
- return front;
- }
+ FormalParameterList *finish(MemoryPool *pool);
// attributes
- SourceElement *element;
- SourceElements *next;
+ PatternElement *element = nullptr;
+ FormalParameterList *next;
};
-class QML_PARSER_EXPORT FunctionBody: public Node
+class QML_PARSER_EXPORT ClassExpression : public ExpressionNode
{
public:
- QQMLJS_DECLARE_AST_NODE(FunctionBody)
+ QQMLJS_DECLARE_AST_NODE(ClassExpression)
- FunctionBody(SourceElements *elts):
- elements (elts)
+ ClassExpression(const QStringRef &n, ExpressionNode *heritage, ClassElementList *elements)
+ : name(n), heritage(heritage), elements(elements)
{ kind = K; }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return elements ? elements->firstSourceLocation() : SourceLocation(); }
+ { return classToken; }
SourceLocation lastSourceLocation() const override
- { return elements ? elements->lastSourceLocation() : SourceLocation(); }
+ { return rbraceToken; }
+
+ ClassExpression *asClassDefinition() override;
// attributes
- SourceElements *elements;
+ QStringRef name;
+ ExpressionNode *heritage;
+ ClassElementList *elements;
+ SourceLocation classToken;
+ SourceLocation identifierToken;
+ SourceLocation lbraceToken;
+ SourceLocation rbraceToken;
};
-class QML_PARSER_EXPORT Program: public Node
+class QML_PARSER_EXPORT ClassDeclaration: public ClassExpression
{
public:
- QQMLJS_DECLARE_AST_NODE(Program)
+ QQMLJS_DECLARE_AST_NODE(ClassDeclaration)
- Program(SourceElements *elts):
- elements (elts)
+ ClassDeclaration(const QStringRef &n, ExpressionNode *heritage, ClassElementList *elements)
+ : ClassExpression(n, heritage, elements)
{ kind = K; }
void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return elements ? elements->firstSourceLocation() : SourceLocation(); }
-
- SourceLocation lastSourceLocation() const override
- { return elements ? elements->lastSourceLocation() : SourceLocation(); }
-
-// attributes
- SourceElements *elements;
};
-class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement
+
+class QML_PARSER_EXPORT ClassElementList : public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(FunctionSourceElement)
+ QQMLJS_DECLARE_AST_NODE(ClassElementList)
- FunctionSourceElement(FunctionDeclaration *f):
- declaration (f)
- { kind = K; }
+ ClassElementList(PatternProperty *property, bool isStatic)
+ : isStatic(isStatic), property(property)
+ {
+ kind = K;
+ next = this;
+ }
+
+ ClassElementList *append(ClassElementList *n) {
+ n->next = next;
+ next = n;
+ return n;
+ }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return declaration->firstSourceLocation(); }
+ { return property->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return declaration->lastSourceLocation(); }
+ {
+ if (next)
+ return next->lastSourceLocation();
+ return property->lastSourceLocation();
+ }
-// attributes
- FunctionDeclaration *declaration;
+ ClassElementList *finish();
+
+ bool isStatic;
+ ClassElementList *next;
+ PatternProperty *property;
};
-class QML_PARSER_EXPORT StatementSourceElement: public SourceElement
+class QML_PARSER_EXPORT Program: public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(StatementSourceElement)
+ QQMLJS_DECLARE_AST_NODE(Program)
- StatementSourceElement(Statement *stmt):
- statement (stmt)
- { kind = K; }
+ Program(StatementList *statements)
+ : statements(statements)
+ { kind = K; }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return statement->firstSourceLocation(); }
+ { return statements ? statements->firstSourceLocation() : SourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return statement->lastSourceLocation(); }
+ { return statements ? statements->lastSourceLocation() : SourceLocation(); }
// attributes
- Statement *statement;
+ StatementList *statements;
};
class QML_PARSER_EXPORT DebuggerStatement: public Statement
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index 140a757e51..406a06c6db 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -89,22 +89,27 @@ class IdentifierExpression;
class NullExpression;
class TrueLiteral;
class FalseLiteral;
+class SuperLiteral;
class NumericLiteral;
class StringLiteral;
+class TemplateLiteral;
class RegExpLiteral;
-class ArrayLiteral;
-class ObjectLiteral;
-class ElementList;
+class Pattern;
+class ArrayPattern;
+class ObjectPattern;
+class PatternElement;
+class PatternElementList;
+class PatternProperty;
+class PatternPropertyList;
class Elision;
-class PropertyAssignmentList;
-class PropertyGetterSetter;
-class PropertyNameAndValue;
class PropertyName;
class IdentifierPropertyName;
class StringLiteralPropertyName;
class NumericLiteralPropertyName;
+class ComputedPropertyName;
class ArrayMemberExpression;
class FieldMemberExpression;
+class TaggedTemplate;
class NewMemberExpression;
class NewExpression;
class CallExpression;
@@ -123,20 +128,19 @@ class NotExpression;
class BinaryExpression;
class ConditionalExpression;
class Expression; // ### rename
+class YieldExpression;
class Block;
+class LeftHandSideExpression;
class StatementList;
class VariableStatement;
class VariableDeclarationList;
-class VariableDeclaration;
class EmptyStatement;
class ExpressionStatement;
class IfStatement;
class DoWhileStatement;
class WhileStatement;
class ForStatement;
-class LocalForStatement;
class ForEachStatement;
-class LocalForEachStatement;
class ContinueStatement;
class BreakStatement;
class ReturnStatement;
@@ -154,14 +158,12 @@ class Finally;
class FunctionDeclaration;
class FunctionExpression;
class FormalParameterList;
-class FunctionBody;
class Program;
-class SourceElements;
-class SourceElement;
-class FunctionSourceElement;
-class StatementSourceElement;
class DebuggerStatement;
class NestedExpression;
+class ClassExpression;
+class ClassDeclaration;
+class ClassElementList;
// ui elements
class UiProgram;
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index 13218f0e98..6e1fed9dbb 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -122,35 +122,41 @@ public:
virtual bool visit(FalseLiteral *) { return true; }
virtual void endVisit(FalseLiteral *) {}
+ virtual bool visit(SuperLiteral *) { return true; }
+ virtual void endVisit(SuperLiteral *) {}
+
virtual bool visit(StringLiteral *) { return true; }
virtual void endVisit(StringLiteral *) {}
+ virtual bool visit(TemplateLiteral *) { return true; }
+ virtual void endVisit(TemplateLiteral *) {}
+
virtual bool visit(NumericLiteral *) { return true; }
virtual void endVisit(NumericLiteral *) {}
virtual bool visit(RegExpLiteral *) { return true; }
virtual void endVisit(RegExpLiteral *) {}
- virtual bool visit(ArrayLiteral *) { return true; }
- virtual void endVisit(ArrayLiteral *) {}
+ virtual bool visit(ArrayPattern *) { return true; }
+ virtual void endVisit(ArrayPattern *) {}
- virtual bool visit(ObjectLiteral *) { return true; }
- virtual void endVisit(ObjectLiteral *) {}
+ virtual bool visit(ObjectPattern *) { return true; }
+ virtual void endVisit(ObjectPattern *) {}
- virtual bool visit(ElementList *) { return true; }
- virtual void endVisit(ElementList *) {}
+ virtual bool visit(PatternElementList *) { return true; }
+ virtual void endVisit(PatternElementList *) {}
- virtual bool visit(Elision *) { return true; }
- virtual void endVisit(Elision *) {}
+ virtual bool visit(PatternPropertyList *) { return true; }
+ virtual void endVisit(PatternPropertyList *) {}
- virtual bool visit(PropertyAssignmentList *) { return true; }
- virtual void endVisit(PropertyAssignmentList *) {}
+ virtual bool visit(PatternElement *) { return true; }
+ virtual void endVisit(PatternElement *) {}
- virtual bool visit(PropertyNameAndValue *) { return true; }
- virtual void endVisit(PropertyNameAndValue *) {}
+ virtual bool visit(PatternProperty *) { return true; }
+ virtual void endVisit(PatternProperty *) {}
- virtual bool visit(PropertyGetterSetter *) { return true; }
- virtual void endVisit(PropertyGetterSetter *) {}
+ virtual bool visit(Elision *) { return true; }
+ virtual void endVisit(Elision *) {}
virtual bool visit(NestedExpression *) { return true; }
virtual void endVisit(NestedExpression *) {}
@@ -164,12 +170,18 @@ public:
virtual bool visit(NumericLiteralPropertyName *) { return true; }
virtual void endVisit(NumericLiteralPropertyName *) {}
+ virtual bool visit(ComputedPropertyName *) { return true; }
+ virtual void endVisit(ComputedPropertyName *) {}
+
virtual bool visit(ArrayMemberExpression *) { return true; }
virtual void endVisit(ArrayMemberExpression *) {}
virtual bool visit(FieldMemberExpression *) { return true; }
virtual void endVisit(FieldMemberExpression *) {}
+ virtual bool visit(TaggedTemplate *) { return true; }
+ virtual void endVisit(TaggedTemplate *) {}
+
virtual bool visit(NewMemberExpression *) { return true; }
virtual void endVisit(NewMemberExpression *) {}
@@ -236,9 +248,6 @@ public:
virtual bool visit(VariableDeclarationList *) { return true; }
virtual void endVisit(VariableDeclarationList *) {}
- virtual bool visit(VariableDeclaration *) { return true; }
- virtual void endVisit(VariableDeclaration *) {}
-
virtual bool visit(EmptyStatement *) { return true; }
virtual void endVisit(EmptyStatement *) {}
@@ -257,15 +266,9 @@ public:
virtual bool visit(ForStatement *) { return true; }
virtual void endVisit(ForStatement *) {}
- virtual bool visit(LocalForStatement *) { return true; }
- virtual void endVisit(LocalForStatement *) {}
-
virtual bool visit(ForEachStatement *) { return true; }
virtual void endVisit(ForEachStatement *) {}
- virtual bool visit(LocalForEachStatement *) { return true; }
- virtual void endVisit(LocalForEachStatement *) {}
-
virtual bool visit(ContinueStatement *) { return true; }
virtual void endVisit(ContinueStatement *) {}
@@ -275,6 +278,9 @@ public:
virtual bool visit(ReturnStatement *) { return true; }
virtual void endVisit(ReturnStatement *) {}
+ virtual bool visit(YieldExpression *) { return true; }
+ virtual void endVisit(YieldExpression *) {}
+
virtual bool visit(WithStatement *) { return true; }
virtual void endVisit(WithStatement *) {}
@@ -317,20 +323,17 @@ public:
virtual bool visit(FormalParameterList *) { return true; }
virtual void endVisit(FormalParameterList *) {}
- virtual bool visit(FunctionBody *) { return true; }
- virtual void endVisit(FunctionBody *) {}
-
- virtual bool visit(Program *) { return true; }
- virtual void endVisit(Program *) {}
+ virtual bool visit(ClassExpression *) { return true; }
+ virtual void endVisit(ClassExpression *) {}
- virtual bool visit(SourceElements *) { return true; }
- virtual void endVisit(SourceElements *) {}
+ virtual bool visit(ClassDeclaration *) { return true; }
+ virtual void endVisit(ClassDeclaration *) {}
- virtual bool visit(FunctionSourceElement *) { return true; }
- virtual void endVisit(FunctionSourceElement *) {}
+ virtual bool visit(ClassElementList *) { return true; }
+ virtual void endVisit(ClassElementList *) {}
- virtual bool visit(StatementSourceElement *) { return true; }
- virtual void endVisit(StatementSourceElement *) {}
+ virtual bool visit(Program *) { return true; }
+ virtual void endVisit(Program *) {}
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp
index b4f0debf85..97ce6ebea3 100644
--- a/src/qml/parser/qqmljsengine_p.cpp
+++ b/src/qml/parser/qqmljsengine_p.cpp
@@ -112,13 +112,6 @@ double integerFromString(const char *buf, int size, int radix)
return result;
}
-double integerFromString(const QString &str, int radix)
-{
- QByteArray ba = QStringRef(&str).trimmed().toLatin1();
- return integerFromString(ba.constData(), ba.size(), radix);
-}
-
-
Engine::Engine()
: _lexer(nullptr), _directives(nullptr)
{ }
diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h
index af26bac0ff..1de907d296 100644
--- a/src/qml/parser/qqmljsengine_p.h
+++ b/src/qml/parser/qqmljsengine_p.h
@@ -63,9 +63,35 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
class Lexer;
-class Directives;
class MemoryPool;
+class QML_PARSER_EXPORT Directives {
+public:
+ virtual ~Directives() {}
+
+ virtual void pragmaLibrary()
+ {
+ }
+
+ virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
+ {
+ Q_UNUSED(jsfile);
+ Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
+ }
+
+ virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
+ {
+ Q_UNUSED(uri);
+ Q_UNUSED(version);
+ Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
+ }
+};
+
+
class QML_PARSER_EXPORT DiagnosticMessage
{
public:
diff --git a/src/qml/parser/qqmljsgrammar.cpp b/src/qml/parser/qqmljsgrammar.cpp
deleted file mode 100644
index 2aaeb385e3..0000000000
--- a/src/qml/parser/qqmljsgrammar.cpp
+++ /dev/null
@@ -1,1167 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// This file was generated by qlalr - DO NOT EDIT!
-#include "qqmljsgrammar_p.h"
-
-QT_BEGIN_NAMESPACE
-
-const char *const QQmlJSGrammar::spell [] = {
- "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue",
- "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===",
- "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier",
- "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=",
- "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=",
- "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return",
- ")", ";", nullptr, "*", "*=", "string literal", "property", "signal", "readonly", "switch",
- "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^",
- "^=", "null", "true", "false", "const", "let", "debugger", "reserved word", "multiline string literal", "comment",
- nullptr, "enum", "public", "import", "pragma", "as", "on", "get", "set", nullptr,
- nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
-};
-
-const short QQmlJSGrammar::lhs [] = {
- 108, 108, 108, 108, 108, 108, 109, 115, 115, 118,
- 118, 118, 118, 121, 123, 119, 119, 120, 120, 120,
- 120, 120, 120, 120, 120, 124, 125, 117, 116, 128,
- 128, 129, 129, 130, 130, 127, 113, 113, 113, 113,
- 132, 132, 132, 132, 132, 132, 132, 113, 140, 140,
- 140, 140, 141, 141, 142, 142, 113, 113, 113, 113,
- 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
- 113, 113, 113, 113, 113, 113, 113, 145, 145, 145,
- 145, 126, 126, 126, 126, 126, 126, 126, 146, 146,
- 146, 146, 146, 146, 146, 146, 146, 146, 146, 146,
- 146, 146, 146, 146, 146, 146, 131, 148, 148, 148,
- 148, 147, 147, 152, 152, 152, 150, 150, 153, 153,
- 153, 153, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 157, 157, 122, 122, 122,
- 122, 122, 160, 160, 161, 161, 161, 161, 159, 159,
- 162, 162, 163, 163, 164, 164, 164, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 166, 166, 166,
- 166, 167, 167, 167, 168, 168, 168, 168, 169, 169,
- 169, 169, 169, 169, 169, 170, 170, 170, 170, 170,
- 170, 171, 171, 171, 171, 171, 172, 172, 172, 172,
- 172, 173, 173, 174, 174, 175, 175, 176, 176, 177,
- 177, 178, 178, 179, 179, 180, 180, 181, 181, 182,
- 182, 183, 183, 184, 184, 151, 151, 185, 185, 186,
- 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- 186, 111, 111, 187, 187, 188, 188, 189, 189, 110,
- 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
- 110, 110, 110, 110, 133, 198, 198, 197, 197, 144,
- 144, 199, 199, 199, 200, 200, 202, 202, 201, 203,
- 206, 204, 204, 207, 205, 205, 134, 135, 135, 136,
- 136, 190, 190, 190, 190, 190, 190, 190, 190, 191,
- 191, 191, 191, 192, 192, 192, 192, 193, 193, 137,
- 138, 208, 208, 211, 211, 209, 209, 212, 210, 194,
- 195, 195, 139, 139, 139, 213, 214, 196, 196, 215,
- 143, 158, 158, 216, 216, 155, 155, 154, 154, 217,
- 114, 114, 218, 218, 112, 112, 149, 149, 219
-};
-
-const short QQmlJSGrammar::rhs [] = {
- 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
- 1, 2, 2, 1, 1, 2, 2, 2, 2, 3,
- 3, 5, 5, 4, 4, 2, 2, 0, 1, 1,
- 2, 1, 3, 2, 3, 2, 1, 5, 4, 4,
- 1, 1, 1, 1, 1, 1, 1, 3, 1, 1,
- 1, 3, 0, 1, 2, 4, 6, 6, 3, 3,
- 7, 7, 4, 4, 5, 5, 8, 8, 5, 6,
- 6, 10, 6, 7, 1, 1, 5, 1, 3, 3,
- 5, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
- 3, 4, 5, 3, 4, 3, 1, 1, 2, 3,
- 4, 1, 2, 3, 7, 8, 1, 3, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,
- 3, 5, 1, 2, 4, 4, 4, 3, 0, 1,
- 1, 3, 1, 1, 1, 2, 2, 1, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 1, 3, 3,
- 3, 1, 3, 3, 1, 3, 3, 3, 1, 3,
- 3, 3, 3, 3, 3, 1, 3, 3, 3, 3,
- 3, 1, 3, 3, 3, 3, 1, 3, 3, 3,
- 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
- 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
- 3, 1, 5, 1, 5, 1, 3, 1, 3, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 3, 0, 1, 1, 3, 0, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 3, 1, 2, 0, 1, 3,
- 3, 1, 1, 1, 1, 3, 1, 3, 2, 2,
- 2, 0, 1, 2, 0, 1, 1, 2, 2, 7,
- 5, 7, 7, 7, 5, 9, 10, 7, 8, 2,
- 2, 3, 3, 2, 2, 3, 3, 3, 3, 5,
- 5, 3, 5, 1, 2, 0, 1, 4, 3, 3,
- 3, 3, 3, 3, 4, 5, 2, 2, 2, 1,
- 8, 8, 7, 1, 3, 0, 1, 0, 1, 1,
- 1, 1, 1, 2, 1, 1, 0, 1, 2
-};
-
-const short QQmlJSGrammar::action_default [] = {
- 0, 0, 28, 0, 0, 0, 28, 0, 195, 262,
- 226, 234, 230, 174, 246, 222, 3, 159, 90, 175,
- 238, 242, 163, 192, 173, 178, 158, 212, 199, 0,
- 97, 98, 93, 0, 87, 82, 367, 0, 0, 0,
- 0, 95, 0, 0, 91, 94, 86, 0, 0, 83,
- 85, 88, 84, 96, 89, 0, 92, 0, 0, 188,
- 0, 0, 175, 194, 177, 176, 0, 0, 0, 190,
- 191, 189, 193, 0, 223, 0, 0, 0, 0, 213,
- 0, 0, 0, 0, 0, 0, 203, 0, 0, 0,
- 197, 198, 196, 201, 205, 204, 202, 200, 215, 214,
- 216, 0, 231, 0, 227, 0, 0, 169, 156, 168,
- 157, 123, 124, 125, 151, 126, 153, 127, 128, 129,
- 130, 131, 132, 133, 134, 135, 136, 137, 138, 152,
- 139, 140, 154, 141, 142, 143, 144, 145, 146, 147,
- 148, 149, 150, 155, 0, 0, 167, 263, 170, 0,
- 171, 0, 172, 166, 0, 259, 252, 250, 257, 258,
- 256, 255, 261, 254, 253, 251, 260, 247, 0, 235,
- 0, 0, 239, 0, 0, 243, 0, 0, 169, 161,
- 0, 160, 0, 165, 179, 0, 356, 356, 357, 0,
- 354, 0, 355, 0, 358, 270, 277, 276, 284, 272,
- 0, 273, 0, 359, 0, 366, 274, 275, 90, 280,
- 278, 363, 360, 365, 281, 0, 293, 0, 0, 0,
- 0, 350, 0, 367, 292, 264, 307, 0, 0, 0,
- 294, 0, 0, 282, 283, 0, 271, 279, 308, 309,
- 0, 356, 0, 0, 358, 0, 351, 352, 0, 340,
- 364, 0, 324, 325, 326, 327, 0, 320, 321, 322,
- 323, 348, 349, 0, 0, 0, 0, 0, 312, 313,
- 314, 268, 266, 228, 236, 232, 248, 224, 269, 0,
- 175, 240, 244, 217, 206, 0, 0, 225, 0, 0,
- 0, 0, 218, 0, 0, 0, 0, 0, 210, 208,
- 211, 209, 207, 220, 219, 221, 0, 233, 0, 229,
- 0, 267, 175, 0, 249, 264, 265, 0, 264, 0,
- 0, 316, 0, 0, 0, 318, 0, 237, 0, 0,
- 241, 0, 0, 245, 305, 0, 297, 306, 300, 0,
- 304, 0, 264, 298, 0, 264, 0, 0, 317, 0,
- 0, 0, 319, 0, 0, 0, 311, 0, 310, 90,
- 117, 368, 0, 0, 122, 286, 289, 0, 123, 293,
- 126, 153, 128, 129, 93, 134, 135, 87, 136, 292,
- 139, 91, 94, 264, 88, 96, 142, 89, 144, 92,
- 146, 147, 294, 149, 150, 155, 0, 119, 118, 121,
- 105, 120, 104, 0, 114, 287, 285, 0, 0, 0,
- 358, 0, 115, 163, 164, 169, 0, 162, 0, 328,
- 329, 0, 356, 0, 0, 358, 0, 116, 0, 0,
- 0, 331, 336, 334, 337, 0, 0, 335, 336, 0,
- 332, 0, 333, 288, 339, 0, 288, 338, 0, 341,
- 342, 0, 288, 343, 344, 0, 0, 345, 0, 0,
- 0, 346, 347, 181, 180, 0, 0, 0, 315, 0,
- 0, 0, 330, 302, 295, 0, 303, 299, 0, 301,
- 290, 0, 291, 296, 0, 0, 358, 0, 353, 108,
- 0, 0, 112, 99, 0, 101, 110, 0, 102, 111,
- 113, 103, 109, 100, 0, 106, 185, 183, 187, 184,
- 182, 186, 361, 6, 362, 4, 2, 75, 107, 0,
- 0, 0, 83, 85, 84, 37, 5, 0, 76, 0,
- 51, 50, 49, 0, 0, 51, 0, 0, 0, 52,
- 0, 67, 68, 0, 65, 0, 66, 41, 42, 43,
- 44, 46, 47, 71, 45, 0, 0, 0, 78, 0,
- 77, 80, 0, 81, 0, 79, 0, 51, 0, 0,
- 0, 0, 0, 61, 0, 62, 0, 0, 32, 0,
- 0, 72, 33, 0, 36, 34, 30, 0, 35, 31,
- 0, 63, 0, 64, 163, 0, 69, 73, 0, 0,
- 0, 0, 163, 288, 0, 70, 90, 123, 293, 126,
- 153, 128, 129, 93, 134, 135, 136, 292, 139, 91,
- 94, 264, 96, 142, 89, 144, 92, 146, 147, 294,
- 149, 150, 155, 74, 0, 59, 53, 60, 54, 0,
- 0, 0, 0, 56, 0, 57, 58, 55, 0, 0,
- 0, 0, 48, 0, 38, 39, 0, 40, 8, 0,
- 0, 9, 0, 11, 0, 10, 0, 1, 27, 15,
- 14, 26, 13, 12, 29, 7, 0, 18, 0, 19,
- 0, 24, 25, 0, 20, 21, 0, 22, 23, 16,
- 17, 369
-};
-
-const short QQmlJSGrammar::goto_default [] = {
- 7, 667, 213, 200, 211, 526, 513, 662, 675, 512,
- 661, 665, 663, 671, 22, 668, 666, 664, 18, 525,
- 587, 577, 584, 579, 553, 195, 199, 201, 206, 237,
- 214, 234, 568, 639, 638, 205, 236, 557, 26, 491,
- 490, 362, 361, 9, 360, 363, 204, 484, 364, 109,
- 17, 149, 24, 13, 148, 19, 25, 59, 23, 8,
- 28, 27, 283, 15, 277, 10, 273, 12, 275, 11,
- 274, 20, 281, 21, 282, 14, 276, 272, 313, 418,
- 278, 279, 207, 197, 196, 210, 209, 233, 198, 367,
- 366, 235, 475, 474, 335, 336, 477, 338, 476, 337,
- 431, 435, 438, 434, 433, 453, 454, 202, 188, 203,
- 212, 0
-};
-
-const short QQmlJSGrammar::action_index [] = {
- 350, 1528, 3041, 3041, 2937, 1235, 115, 105, 239, -108,
- 102, 93, 95, 205, -108, 422, 110, -108, -108, 727,
- 92, 118, 265, 256, -108, -108, -108, 507, 247, 1528,
- -108, -108, -108, 665, -108, -108, 2729, 1826, 1528, 1528,
- 1528, -108, 1041, 1528, -108, -108, -108, 1528, 1528, -108,
- -108, -108, -108, -108, -108, 1528, -108, 1528, 1528, -108,
- 1528, 1528, 174, 221, -108, -108, 1528, 1528, 1528, -108,
- -108, -108, 177, 1528, 422, 1528, 1528, 1528, 1528, 411,
- 1528, 1528, 1528, 1528, 1528, 1528, 211, 1528, 1528, 1528,
- 142, 148, 154, 217, 223, 226, 227, 231, 507, 385,
- 395, 1528, 57, 1528, 83, 2521, 1528, 1528, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, 179, 1528, -108, -108, 77, 36,
- -108, 1528, -108, -108, 1528, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, 1528, 56,
- 1528, 1528, 80, 74, 1528, -108, 2521, 1528, 1528, -108,
- 125, -108, 55, -108, -108, 53, 410, 418, 72, 52,
- -108, 392, -108, 46, 3041, -108, -108, -108, -108, -108,
- 273, -108, 396, -108, 44, -108, -108, -108, 76, -108,
- -108, -108, 3041, -108, -108, 744, -108, 589, 98, 2937,
- 91, 90, 88, 3249, -108, 1528, -108, 86, 1528, 81,
- -108, 75, 73, -108, -108, 586, -108, -108, -108, -108,
- 71, 491, 69, 65, 3041, 64, -108, -108, 2937, -108,
- -108, 139, -108, -108, -108, -108, 134, -108, -108, -108,
- -108, -108, -108, 63, 66, 1528, 147, 264, -108, -108,
- -108, 1726, -108, 87, 68, 70, -108, 334, 82, 78,
- 796, 89, 121, 349, 318, 469, 1528, 330, 1528, 1528,
- 1528, 1528, 359, 1528, 1528, 1528, 1528, 1528, 284, 289,
- 303, 306, 313, 365, 369, 375, 1528, 58, 1528, 85,
- 1528, -108, 849, 1528, -108, 1528, 79, 59, 1528, 61,
- 2937, -108, 1528, 146, 2937, -108, 1528, 62, 1528, 1528,
- 106, 99, 1528, -108, 96, 176, 171, -108, -108, 1528,
- -108, 407, 1528, -108, 97, 1528, -52, 2937, -108, 1528,
- 120, 2937, -108, 1528, 123, 2937, 101, 2937, -108, 116,
- -108, 84, 45, 0, -108, -108, 2937, -39, 641, -3,
- 652, 156, 1528, 2937, -7, -11, 567, 2625, -21, 5,
- 945, 2, 94, 1629, 2625, -1, -26, 6, 1528, 10,
- -15, 1528, 14, 1528, -25, -12, 2833, -108, -108, -108,
- -108, -108, -108, 1528, -108, -108, -108, -14, -58, -13,
- 3041, -36, -108, 287, -108, 1528, -46, -108, 153, -108,
- -108, -31, 586, -57, -32, 3041, 11, -108, 1528, 168,
- 29, -108, 47, -108, 48, 169, 1528, -108, 49, 50,
- -108, 9, -108, 2937, -108, 136, 2937, -108, 275, -108,
- -108, 126, 2937, 35, -108, 33, 37, -108, 466, -4,
- 38, -108, -108, -108, -108, 1528, 130, 2937, -108, 1528,
- 117, 2937, -108, 34, -108, 296, -108, -108, 1528, -108,
- -108, 404, -108, -108, 12, 40, 3041, 13, -108, -108,
- 155, 1926, -108, -108, 2026, -108, -108, 2126, -108, -108,
- -108, -108, -108, -108, 144, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, 3041, -108, -108, -108, 132, -27,
- 15, 1137, 218, -23, 17, -108, -108, 196, -108, 242,
- 8, -108, -108, 579, 237, -108, 127, 18, 415, -108,
- 103, -108, -108, 236, -108, 2223, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, 27, 21, 137, 31, 20,
- -108, 23, -5, -108, -9, -108, 332, -10, 571, 201,
- 195, 586, 225, -108, 7, -108, 1137, 165, -108, 4,
- 1137, -108, -108, 1333, -108, -108, -108, 1431, -108, -108,
- 184, -108, 2223, -108, 331, 3, -108, -108, 202, 531,
- 26, 2417, 327, 3145, 1, -108, 25, 629, 24, 649,
- 124, 1528, 2937, 22, -6, 514, -8, 19, 1041, 16,
- 94, 1629, 28, 42, 67, 1528, 60, 43, 1528, 54,
- 1528, 41, 39, -108, 234, -108, 228, -108, 51, -2,
- 564, 233, 575, -108, 100, -108, -108, -108, 2320, 1137,
- 1826, 32, -108, 122, -108, -108, 30, -108, -108, 1137,
- 1137, 104, 903, -108, 308, -108, 108, -108, -108, 133,
- 119, -108, -108, -108, -108, -108, 451, -108, 164, -108,
- 161, -108, -108, 458, -108, -108, 151, -108, -108, -108,
- -108, -108,
-
- -112, 18, 86, 97, 69, 316, 7, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -64,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, 66,
- -112, -112, -112, -17, -112, -112, -10, -36, 3, 90,
- 95, -112, 149, 74, -112, -112, -112, 67, 13, -112,
- -112, -112, -112, -112, -112, 178, -112, 181, 185, -112,
- 189, 190, -112, -112, -112, -112, 198, 208, 212, -112,
- -112, -112, -112, 209, -112, 201, 164, 111, 113, -112,
- 116, 130, 131, 132, 134, 142, -112, 118, 124, 144,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, 154, -112, 155, -112, 268, 28, -8, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, 42, -112, -112, -112, -112,
- -112, 47, -112, -112, 50, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, 159, -112,
- 158, 56, -112, -112, 57, -112, 362, 60, 157, -112,
- -112, -112, -112, -112, -112, -112, 20, 151, -112, -112,
- -112, 25, -112, -112, 30, -112, -112, -112, -112, -112,
- -112, -112, 31, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, 233, -112, -112, 34, -112, 35, -112, 225,
- -112, 36, -112, 216, -112, 55, -112, -112, 53, 39,
- -112, -112, -112, -112, -112, 19, -112, -112, -112, -112,
- -112, 94, -112, -112, 92, -112, -112, -112, 117, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, 33, -112, -112, -112, -112,
- -112, 88, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, 51, 220, -112, 227, 235,
- 236, 244, -112, 21, 17, 102, 91, 89, -112, -112,
- -112, -112, -112, -112, -112, -112, 211, -112, 247, -112,
- 257, -112, -112, 264, -112, 75, -112, -112, 103, -112,
- 112, -112, 29, -112, 73, -112, 263, -112, 255, 254,
- -112, -112, 245, -112, -112, -112, -112, -112, -112, 248,
- -112, 65, 105, -112, -112, 99, -112, 162, -112, 37,
- -112, 115, -112, 44, -112, 135, -112, 137, -112, -112,
- -112, -112, -112, -112, -112, -112, 138, -112, 24, -112,
- 26, -112, 104, 140, -112, -112, 32, 64, -112, -112,
- 174, -112, -112, 48, 87, -112, -112, -112, 54, -112,
- 40, 71, -112, 150, -112, -112, 197, -112, -112, -112,
- -112, -112, -112, 12, -112, -112, -112, -112, -112, -112,
- 206, -112, -112, -112, -112, 207, -112, -112, -112, -112,
- -112, -112, 231, -112, -112, 239, -112, -112, 43, -112,
- -112, -112, -112, -112, -59, -112, 38, -112, -62, -112,
- -112, -112, -112, 258, -112, -112, 259, -112, -112, -112,
- -112, -112, 163, -72, -112, -112, 41, -112, 62, -112,
- 61, -112, -112, -112, -112, 59, -112, 193, -112, 58,
- -112, 204, -112, -112, -112, -112, -112, -112, 52, -112,
- -112, 175, -112, -112, -112, -112, 186, -112, -112, -112,
- -112, 49, -112, -112, 173, -112, -112, 45, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, 213, -112, -112, -112, -112, -112,
- -112, 46, -112, -112, -112, -112, -112, -112, -112, 27,
- -112, -112, -112, -18, -9, -112, -112, -112, 15, -112,
- -112, -112, -112, -112, -112, 331, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, 11, -6,
- -112, 10, -112, -112, -112, -112, 156, -112, -112, -112,
- 240, -112, -112, 330, -112, -112, -112, 332, -112, -112,
- -112, -112, 376, -112, -112, 8, -112, -112, -7, 76,
- -112, 358, -112, 228, 5, -112, -112, 6, -112, 4,
- -112, 79, 221, -112, -112, 2, -112, -112, 174, -112,
- -112, 16, -112, -112, -112, 14, -112, -16, 70, -112,
- 63, -112, -112, -112, -112, -112, -30, -112, -112, -112,
- -15, -28, -13, -112, -112, -112, -112, -112, 460, 93,
- 307, -12, -112, -112, -112, -112, -11, -112, -112, -2,
- -1, 85, 84, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -3, -112, -112, -112,
- -112, -112, -112, 0, -112, -112, -112, -112, -112, -112,
- -112, -112
-};
-
-const short QQmlJSGrammar::action_info [] = {
- -132, 425, 409, 424, -151, 422, -120, 403, 347, -140,
- 428, 465, -152, -143, 417, 353, 406, -145, 452, 412,
- 410, -148, 408, -140, 469, 271, -152, 569, 353, -132,
- 271, -151, 248, 601, 583, -120, 583, 583, 565, 529,
- 562, 576, 563, 598, 555, 534, 634, 539, 564, 561,
- 558, 478, 436, 436, 436, 456, 460, 443, 644, 641,
- 556, -148, 432, 583, 442, 583, 427, -145, 488, 458,
- 452, 452, 485, 486, -143, 469, 452, 465, 428, 194,
- 191, 174, 168, 248, 73, 151, 286, 145, 286, 187,
- 310, 326, 396, 0, 168, 0, 153, 0, 244, 247,
- 402, -121, 265, 73, 101, 691, 332, 241, 326, 469,
- 306, 465, 193, 339, 452, 183, 306, 357, 145, 246,
- 318, 320, 428, 248, 353, 145, 186, 271, 145, 243,
- 580, 145, 455, 145, 176, 0, 103, 308, 145, 315,
- 264, 101, 537, 446, 145, 559, 456, 176, 176, 308,
- 0, 538, 145, 177, 145, 145, 0, 0, 345, 262,
- 261, 646, 645, 494, 542, 541, 177, 177, 170, 690,
- 689, 328, 171, 580, 103, 329, 145, 471, 654, 439,
- 351, 181, 60, 355, 341, 262, 261, 145, 60, 66,
- 467, 592, 560, 61, 60, 260, 259, 659, 660, 61,
- 255, 254, 349, 648, 505, 61, 324, 267, 659, 660,
- 537, 495, 688, 687, 420, 419, 64, 262, 261, 571,
- 105, 581, 682, 681, 440, 685, 684, 65, 430, 583,
- 535, 535, 574, 66, 67, 146, 87, 342, 88, 106,
- 68, 107, 87, 545, 88, 593, 591, 567, 87, 89,
- 88, 87, 87, 88, 88, 89, 87, 535, 88, 683,
- 0, 89, 535, 0, 89, 89, 535, 0, 66, 89,
- 636, 530, 87, 0, 88, 0, 532, 532, 67, 60,
- 176, 145, 0, 145, 68, 89, 575, 573, 531, 531,
- 61, 0, 649, 532, 0, 637, 635, 546, 544, 177,
- 0, 178, 176, 532, 481, 531, 0, 0, 532, 87,
- 0, 88, 532, 67, 87, 531, 88, 532, 0, 68,
- 531, 177, 89, 415, 531, 270, 268, 89, 87, 531,
- 88, 87, 0, 88, 239, 238, 450, 449, 87, 0,
- 88, 89, 176, 87, 89, 88, 176, 176, 288, 289,
- 0, 89, 288, 289, 269, 678, 89, 482, 480, 0,
- -107, 177, 0, 178, -107, 177, 177, 178, 415, 679,
- 677, 0, 293, 294, 0, 290, 291, 0, 0, 290,
- 291, 295, 293, 294, 296, 0, 297, 0, 293, 294,
- 0, 295, 293, 294, 296, 0, 297, 295, 293, 294,
- 296, 295, 297, 676, 296, 0, 297, 295, 80, 81,
- 296, 0, 297, 0, 0, 0, 82, 83, 80, 81,
- 84, 35, 85, 0, 0, 35, 82, 83, 0, 0,
- 84, 0, 85, 35, 80, 81, 35, 0, 0, 35,
- 75, 76, 82, 83, 35, 0, 84, 35, 85, 0,
- 6, 5, 4, 1, 3, 2, 0, 0, 49, 52,
- 50, 0, 49, 52, 50, 0, 0, 77, 78, 0,
- 49, 52, 50, 49, 52, 50, 49, 52, 50, 0,
- 35, 49, 52, 50, 49, 52, 50, 35, 46, 34,
- 51, 0, 46, 34, 51, 35, 0, 0, 35, 0,
- 46, 34, 51, 46, 34, 51, 46, 34, 51, 0,
- 0, 46, 34, 51, 46, 34, 51, 49, 52, 50,
- 35, 0, 0, 0, 49, 52, 50, 0, 0, 0,
- 80, 81, 49, 52, 50, 49, 52, 50, 82, 83,
- 0, 0, 84, 35, 85, 0, 537, 46, 34, 51,
- 186, 0, 0, 0, 46, 34, 51, 49, 52, 50,
- 35, 0, 46, 34, 51, 46, 34, 51, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 537,
- 49, 52, 50, 0, 0, 0, 537, 46, 34, 51,
- 537, 0, 0, 35, 537, 0, 35, 49, 52, 50,
- 35, 0, 0, 186, 35, 0, 0, 0, 35, 0,
- 46, 34, 51, 0, 0, 35, 0, 0, 35, 0,
- 0, 0, 0, 0, 0, 0, 0, 46, 34, 51,
- 49, 52, 50, 49, 52, 50, 0, 49, 52, 50,
- 0, 49, 52, 50, 0, 49, 52, 50, 0, 0,
- 258, 257, 49, 52, 50, 49, 52, 50, 35, 0,
- 46, 34, 51, 46, 34, 51, 0, 46, 34, 51,
- 35, 46, 34, 51, 0, 46, 34, 51, 35, 0,
- 0, 35, 46, 34, 51, 46, 34, 51, 0, 0,
- 253, 252, 0, 0, 35, 49, 52, 50, 0, 0,
- 0, 186, 253, 252, 0, 0, 0, 49, 52, 50,
- 258, 257, 0, 258, 257, 49, 52, 50, 49, 52,
- 50, 0, 0, 0, 0, 46, 34, 51, 0, 0,
- 155, 49, 52, 50, 0, 0, 0, 46, 34, 51,
- 156, 0, 0, 0, 157, 46, 34, 51, 46, 34,
- 51, 0, 0, 158, 0, 159, 0, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 160, 0, 161, 64,
- 0, 0, 0, 35, 0, 0, 162, 0, 0, 163,
- 65, 0, 0, 0, 0, 164, 0, 0, 0, 0,
- 0, 165, 0, 0, 0, 0, 0, 0, 0, 155,
- 0, 0, 0, 0, 0, 253, 252, 166, 0, 156,
- 49, 52, 50, 157, 0, 0, 0, 0, 0, 0,
- 0, 0, 158, 0, 159, 0, 0, 322, 0, 0,
- 0, 0, 0, 0, 0, 160, 0, 161, 64, 0,
- 46, 34, 51, 0, 0, 162, 0, 0, 163, 65,
- 0, 0, 155, 0, 164, 0, 0, 0, 0, 0,
- 165, 0, 156, 0, 0, 0, 157, 0, 0, 0,
- 0, 0, 0, 0, 0, 158, 166, 159, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 160, 0,
- 161, 64, 0, 0, 0, 0, 0, 0, 162, 0,
- 0, 163, 65, 0, 0, 0, 0, 164, 0, 0,
- 0, 0, 0, 165, 0, 30, 31, 0, 0, 0,
- 0, 0, 0, 0, 0, 33, 0, 0, 0, 166,
- 0, 0, 35, 0, 0, 0, 36, 37, 0, 38,
- 0, 0, 0, 0, 0, 0, 521, 0, 0, 0,
- 45, 0, 0, 0, 0, 0, 0, 30, 31, 0,
- 0, 0, 0, 0, 0, 0, 0, 33, 53, 49,
- 52, 50, 0, 54, 35, 0, 0, 0, 36, 37,
- 0, 38, 0, 0, 44, 56, 32, 0, 42, 0,
- 0, 41, 45, 0, 0, 0, 0, 0, 0, 46,
- 34, 51, 0, 0, 0, 0, 0, 0, 0, 0,
- 53, 49, 52, 50, 0, 54, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 44, 56, 32, 0,
- 0, 0, 0, 41, 0, 0, 0, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 30, 31, 0, 0, 0, 0, 0,
- 0, 0, 0, 33, 0, 0, 0, 0, 0, 0,
- 35, 0, 0, 0, 36, 37, 0, 38, 0, 0,
- 0, 0, 0, 0, 42, 0, 0, 0, 45, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 53, 49, 52, 50,
- 0, 54, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 44, 56, 32, 0, 0, 0, 0, 41,
- 0, 0, 0, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,
- 31, 0, 0, 0, 0, 0, 0, 0, 0, 33,
- 0, 0, 0, 0, 0, 0, 35, 0, 0, 0,
- 36, 37, 0, 38, 0, 0, 0, 0, 0, 0,
- 521, 0, 0, 0, 45, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 53, 49, 52, 50, 0, 54, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 44, 56,
- 32, 0, 0, 0, 0, 41, 0, 0, 0, 0,
- 0, 0, 0, 46, 34, 51, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 519, 0, 30, 31, 0,
- 0, 0, 0, 0, 0, 0, 0, 221, 0, 0,
- 0, 0, 0, 0, 35, 0, 0, 0, 36, 37,
- 0, 38, 0, 0, 0, 0, 0, 0, 521, 0,
- 0, 0, 45, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 53, 522, 524, 523, 0, 54, 0, 0, 0, 0,
- 230, 0, 0, 0, 0, 0, 44, 56, 32, 216,
- 224, 0, 0, 41, 0, 0, 520, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 519, 0, 30, 31, 0, 0, 0,
- 0, 0, 0, 0, 0, 221, 0, 0, 0, 0,
- 0, 0, 35, 0, 0, 0, 36, 37, 0, 38,
- 0, 0, 0, 0, 0, 0, 521, 0, 0, 0,
- 45, 0, 0, 0, 0, 0, 0, 0, 585, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 53, 522,
- 524, 523, 0, 54, 0, 0, 0, 0, 230, 0,
- 0, 0, 0, 0, 44, 56, 32, 216, 224, 0,
- 0, 41, 0, 0, 520, 0, 0, 0, 0, 46,
- 34, 51, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 519, 0, 30, 31, 0, 0, 0, 0, 0,
- 0, 0, 0, 221, 0, 0, 0, 0, 0, 0,
- 35, 0, 0, 0, 36, 37, 0, 38, 0, 0,
- 0, 0, 0, 0, 521, 0, 0, 0, 45, 0,
- 0, 0, 0, 0, 0, 0, 588, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 53, 522, 524, 523,
- 0, 54, 0, 0, 0, 0, 230, 0, 0, 0,
- 0, 0, 44, 56, 32, 216, 224, 0, 0, 41,
- 0, 0, 520, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 29,
- 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
- 33, 0, 0, 0, 0, 0, 0, 35, 0, 0,
- 0, 36, 37, 0, 38, 0, 0, 0, 39, 0,
- 40, 42, 43, 0, 0, 45, 0, 0, 0, 47,
- 0, 48, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 53, 49, 52, 50, 0, 54, 0,
- 55, 0, 57, 0, 58, 0, 0, 0, 0, 44,
- 56, 32, 0, 0, 0, 0, 41, 0, 0, 0,
- 0, 0, 0, 0, 46, 34, 51, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, -141, 0, 0, 0,
- 29, 30, 31, 0, 0, 0, 0, 0, 0, 0,
- 0, 33, 0, 0, 0, 0, 0, 0, 35, 0,
- 0, 0, 36, 37, 0, 38, 0, 0, 0, 39,
- 0, 40, 42, 43, 0, 0, 45, 0, 0, 0,
- 47, 0, 48, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 53, 49, 52, 50, 0, 54,
- 0, 55, 0, 57, 0, 58, 0, 0, 0, 0,
- 44, 56, 32, 0, 0, 0, 0, 41, 0, 0,
- 0, 0, 0, 0, 0, 46, 34, 51, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 285, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 492, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 493, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 500, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 503, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 492, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 498, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 500, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 501, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 29, 30, 31, 0, 0, 0,
- 0, 0, 0, 0, 0, 33, 0, 0, 0, 0,
- 0, 0, 35, 222, 0, 0, 223, 37, 0, 38,
- 0, 0, 0, 39, 0, 40, 42, 43, 0, 0,
- 45, 0, 0, 0, 47, 0, 48, 0, 0, 0,
- 0, 0, 0, 0, 226, 0, 0, 0, 53, 49,
- 52, 50, 227, 54, 0, 55, 229, 57, 0, 58,
- 0, 232, 0, 0, 44, 56, 32, 0, 0, 0,
- 0, 41, 0, 0, 0, 0, 0, 0, 0, 46,
- 34, 51, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 29, 30, 31, 0, 0, 0, 0, 0, 0,
- 0, 0, 33, 0, 0, 0, 0, 0, 0, 35,
- 222, 0, 0, 603, 650, 0, 38, 0, 0, 0,
- 39, 0, 40, 42, 43, 0, 0, 45, 0, 0,
- 0, 47, 0, 48, 0, 0, 0, 0, 0, 0,
- 0, 226, 0, 0, 0, 53, 49, 52, 50, 227,
- 54, 0, 55, 229, 57, 0, 58, 0, 232, 0,
- 0, 44, 56, 32, 0, 0, 0, 0, 41, 0,
- 0, 0, 0, 0, 0, 0, 46, 34, 51, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 29, 30,
- 31, 0, 0, 0, 0, 0, 0, 0, 0, 33,
- 0, 0, 0, 0, 0, 0, 35, 222, 0, 0,
- 603, 37, 0, 38, 0, 0, 0, 39, 0, 40,
- 42, 43, 0, 0, 45, 0, 0, 0, 47, 0,
- 48, 0, 0, 0, 0, 0, 0, 0, 226, 0,
- 0, 0, 53, 49, 52, 50, 227, 54, 0, 55,
- 229, 57, 0, 58, 0, 232, 0, 0, 44, 56,
- 32, 0, 0, 0, 0, 41, 0, 0, 0, 0,
- 0, 0, 0, 46, 34, 51, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 111, 112, 113, 0, 0,
- 115, 117, 118, 0, 0, 119, 0, 120, 0, 0,
- 0, 123, 124, 125, 0, 0, 0, 0, 0, 0,
- 35, 126, 127, 128, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 130, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 133, 0, 0, 0, 0, 0, 0, 49, 52, 50,
- 134, 135, 136, 0, 138, 139, 140, 141, 142, 143,
- 0, 0, 131, 137, 122, 114, 129, 116, 132, 0,
- 0, 0, 121, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 111,
- 112, 113, 0, 0, 115, 117, 118, 0, 0, 119,
- 0, 120, 0, 0, 0, 123, 124, 125, 0, 0,
- 0, 0, 0, 0, 35, 126, 127, 128, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 130, 0,
- 0, 0, 399, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 133, 0, 0, 0, 0, 0,
- 401, 49, 52, 50, 134, 135, 136, 0, 138, 139,
- 140, 141, 142, 143, 0, 0, 131, 137, 122, 114,
- 129, 116, 132, 0, 0, 0, 121, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 111, 112, 113, 0, 0, 115, 117,
- 118, 0, 0, 119, 0, 120, 0, 0, 0, 123,
- 124, 125, 0, 0, 0, 0, 0, 0, 35, 126,
- 127, 128, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 130, 0, 0, 0, 399, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 133, 0,
- 0, 0, 0, 0, 401, 49, 52, 50, 134, 135,
- 136, 0, 138, 139, 140, 141, 142, 143, 0, 0,
- 131, 137, 122, 114, 129, 116, 132, 0, 0, 0,
- 121, 0, 0, 0, 0, 46, 377, 384, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 111, 112, 113,
- 0, 0, 115, 117, 118, 0, 0, 119, 0, 120,
- 0, 0, 0, 123, 124, 125, 0, 0, 0, 0,
- 0, 0, 35, 126, 127, 128, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 130, 0, 0, 0,
- 399, 0, 0, 0, 0, 0, 0, 0, 400, 0,
- 0, 0, 133, 0, 0, 0, 0, 0, 401, 49,
- 52, 50, 134, 135, 136, 0, 138, 139, 140, 141,
- 142, 143, 0, 0, 131, 137, 122, 114, 129, 116,
- 132, 0, 0, 0, 121, 0, 0, 0, 0, 46,
- 377, 384, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 215, 0, 0, 0, 0, 217, 0, 29, 30,
- 31, 219, 0, 0, 0, 0, 0, 0, 220, 33,
- 0, 0, 0, 0, 0, 0, 35, 222, 0, 0,
- 223, 37, 0, 38, 0, 0, 0, 39, 0, 40,
- 42, 43, 0, 0, 45, 0, 0, 0, 47, 0,
- 48, 0, 0, 0, 0, 0, 225, 0, 226, 0,
- 0, 0, 53, 49, 52, 50, 227, 54, 228, 55,
- 229, 57, 230, 58, 231, 232, 0, 0, 44, 56,
- 32, 216, 224, 218, 0, 41, 0, 0, 0, 0,
- 0, 0, 0, 46, 34, 51, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 215, 0, 0, 0, 0,
- 217, 0, 29, 30, 31, 219, 0, 0, 0, 0,
- 0, 0, 220, 221, 0, 0, 0, 0, 0, 0,
- 35, 222, 0, 0, 223, 37, 0, 38, 0, 0,
- 0, 39, 0, 40, 42, 43, 0, 0, 45, 0,
- 0, 0, 47, 0, 48, 0, 0, 0, 0, 0,
- 225, 0, 226, 0, 0, 0, 53, 49, 52, 50,
- 227, 54, 228, 55, 229, 57, 230, 58, 231, 232,
- 0, 0, 44, 56, 32, 216, 224, 218, 0, 41,
- 0, 0, 0, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 607,
- 112, 113, 0, 0, 609, 117, 611, 30, 31, 612,
- 0, 120, 0, 0, 0, 123, 614, 615, 0, 0,
- 0, 0, 0, 0, 35, 616, 127, 128, 223, 37,
- 0, 38, 0, 0, 0, 39, 0, 40, 618, 43,
- 0, 0, 620, 0, 0, 0, 47, 0, 48, 0,
- 0, 0, 0, 0, 621, 0, 226, 0, 0, 0,
- 622, 49, 52, 50, 623, 624, 625, 55, 627, 628,
- 629, 630, 631, 632, 0, 0, 619, 626, 613, 608,
- 617, 610, 132, 41, 0, 0, 121, 0, 0, 0,
- 0, 46, 377, 384, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 368, 112, 113, 0, 0, 370, 117,
- 372, 30, 31, 373, 0, 120, 0, 0, 0, 123,
- 375, 376, 0, 0, 0, 0, 0, 0, 35, 378,
- 127, 128, 223, 37, 0, 38, 0, 0, 0, 39,
- 0, 40, 380, 43, 0, 0, 382, 0, 0, 0,
- 47, 0, 48, 0, -288, 0, 0, 0, 383, 0,
- 226, 0, 0, 0, 385, 49, 52, 50, 386, 387,
- 388, 55, 390, 391, 392, 393, 394, 395, 0, 0,
- 381, 389, 374, 369, 379, 371, 132, 41, 0, 0,
- 121, 0, 0, 0, 0, 46, 377, 384, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-
- 543, 185, 640, 647, 642, 643, 504, 489, 397, 451,
- 655, 657, 669, 670, 154, 680, 658, 448, 686, 316,
- 185, 16, 256, 536, 251, 599, 570, 633, 572, 590,
- 597, 144, 323, 540, 457, 150, 266, 473, 190, 441,
- 350, 445, 251, 192, 256, 437, 429, 354, 208, 240,
- 185, 316, 251, 256, 185, 404, 448, 448, 316, 533,
- 566, 470, 466, 180, 451, 451, 462, 0, 62, 334,
- 510, 516, 62, 0, 0, 325, 62, 299, 316, 0,
- 459, 298, 397, 334, 0, 147, 461, 208, 499, 0,
- 152, 208, 502, 167, 600, 479, 673, 672, 518, 173,
- 175, 515, 316, 674, 208, 397, 316, 518, 316, 407,
- 208, 0, 190, 0, 321, 208, 656, 352, 62, 249,
- 464, 62, 62, 184, 509, 62, 62, 463, 463, 62,
- 208, 508, 421, 208, 62, 208, 184, 356, 245, 358,
- 405, 242, 263, 280, 62, 62, 62, 506, 284, 302,
- 62, 301, 507, 208, 317, 208, 208, 62, 208, 62,
- 343, 184, 300, 413, 348, 365, 62, 0, 62, 190,
- 518, 62, 99, 62, 100, 578, 86, 90, 346, 62,
- 208, 208, 319, 91, 344, 62, 62, 62, 413, 62,
- 93, 94, 95, 473, 96, 468, 514, 62, 189, 62,
- 150, 414, 97, 92, 208, 62, 472, 464, 182, 62,
- 62, 208, 497, 62, 62, 397, 496, 250, 365, 62,
- 104, 102, 208, 263, 208, 98, 414, 263, 169, 172,
- 365, 208, 487, 62, 359, 511, 62, 250, 463, 208,
- 62, 398, 464, 208, 62, 62, 606, 63, 72, 190,
- 150, 208, 411, 62, 518, 69, 62, 208, 416, 582,
- 365, 365, 79, 62, 62, 70, 62, 62, 483, 71,
- 0, 284, 74, 0, 0, 62, 208, 208, 423, 307,
- 284, 0, 62, 0, 287, 426, 108, 284, 0, 292,
- 62, 62, 0, 0, 0, 284, 284, 303, 304, 62,
- 312, 0, 62, 312, 284, 284, 305, 284, 284, 312,
- 62, 0, 312, 309, 284, 284, 110, 284, 62, 312,
- 0, 594, 333, 284, 284, 340, 578, 330, 653, 0,
- 518, 331, 0, 327, 311, 586, 0, 589, 0, 527,
- 0, 314, 0, 0, 518, 0, 518, 444, 447, 0,
- 489, 517, 528, 527, 0, 527, 547, 548, 549, 550,
- 554, 551, 552, 0, 0, 517, 528, 517, 528, 0,
- 0, 0, 602, 0, 0, 0, 0, 0, 0, 0,
- 108, 604, 605, 547, 548, 549, 550, 554, 551, 552,
- 594, 0, 0, 0, 0, 0, 0, 0, 0, 595,
- 596, 547, 548, 549, 550, 554, 551, 552, 0, 0,
- 110, 179, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 602, 0, 0, 0, 0, 0,
- 0, 0, 0, 651, 652, 547, 548, 549, 550, 554,
- 551, 552, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0
-};
-
-const short QQmlJSGrammar::action_check [] = {
- 7, 33, 60, 60, 7, 36, 7, 7, 60, 7,
- 36, 36, 7, 7, 60, 36, 55, 7, 33, 55,
- 33, 7, 36, 7, 36, 36, 7, 37, 36, 7,
- 36, 7, 7, 7, 33, 7, 33, 33, 47, 66,
- 17, 34, 47, 66, 29, 37, 29, 29, 17, 29,
- 29, 17, 5, 5, 5, 20, 60, 7, 60, 8,
- 33, 7, 33, 33, 55, 33, 55, 7, 55, 36,
- 33, 33, 60, 33, 7, 36, 33, 36, 36, 33,
- 8, 7, 2, 7, 1, 8, 1, 8, 1, 36,
- 8, 2, 8, -1, 2, -1, 60, -1, 33, 55,
- 55, 7, 36, 1, 48, 0, 7, 36, 2, 36,
- 48, 36, 60, 17, 33, 60, 48, 16, 8, 55,
- 61, 60, 36, 7, 36, 8, 36, 36, 8, 60,
- 8, 8, 6, 8, 15, -1, 79, 79, 8, 61,
- 77, 48, 15, 7, 8, 8, 20, 15, 15, 79,
- -1, 24, 8, 34, 8, 8, -1, -1, 61, 61,
- 62, 61, 62, 8, 61, 62, 34, 34, 50, 61,
- 62, 50, 54, 8, 79, 54, 8, 60, 56, 10,
- 60, 56, 40, 60, 8, 61, 62, 8, 40, 12,
- 60, 7, 55, 51, 40, 61, 62, 93, 94, 51,
- 61, 62, 31, 7, 60, 51, 60, 60, 93, 94,
- 15, 56, 61, 62, 61, 62, 42, 61, 62, 24,
- 15, 56, 61, 62, 55, 61, 62, 53, 60, 33,
- 29, 29, 7, 12, 57, 56, 25, 61, 27, 34,
- 63, 36, 25, 7, 27, 61, 62, 29, 25, 38,
- 27, 25, 25, 27, 27, 38, 25, 29, 27, 95,
- -1, 38, 29, -1, 38, 38, 29, -1, 12, 38,
- 36, 29, 25, -1, 27, -1, 75, 75, 57, 40,
- 15, 8, -1, 8, 63, 38, 61, 62, 87, 87,
- 51, -1, 96, 75, -1, 61, 62, 61, 62, 34,
- -1, 36, 15, 75, 8, 87, -1, -1, 75, 25,
- -1, 27, 75, 57, 25, 87, 27, 75, -1, 63,
- 87, 34, 38, 36, 87, 61, 62, 38, 25, 87,
- 27, 25, -1, 27, 61, 62, 61, 62, 25, -1,
- 27, 38, 15, 25, 38, 27, 15, 15, 18, 19,
- -1, 38, 18, 19, 90, 47, 38, 61, 62, -1,
- 33, 34, -1, 36, 33, 34, 34, 36, 36, 61,
- 62, -1, 23, 24, -1, 45, 46, -1, -1, 45,
- 46, 32, 23, 24, 35, -1, 37, -1, 23, 24,
- -1, 32, 23, 24, 35, -1, 37, 32, 23, 24,
- 35, 32, 37, 95, 35, -1, 37, 32, 23, 24,
- 35, -1, 37, -1, -1, -1, 31, 32, 23, 24,
- 35, 29, 37, -1, -1, 29, 31, 32, -1, -1,
- 35, -1, 37, 29, 23, 24, 29, -1, -1, 29,
- 18, 19, 31, 32, 29, -1, 35, 29, 37, -1,
- 100, 101, 102, 103, 104, 105, -1, -1, 66, 67,
- 68, -1, 66, 67, 68, -1, -1, 45, 46, -1,
- 66, 67, 68, 66, 67, 68, 66, 67, 68, -1,
- 29, 66, 67, 68, 66, 67, 68, 29, 96, 97,
- 98, -1, 96, 97, 98, 29, -1, -1, 29, -1,
- 96, 97, 98, 96, 97, 98, 96, 97, 98, -1,
- -1, 96, 97, 98, 96, 97, 98, 66, 67, 68,
- 29, -1, -1, -1, 66, 67, 68, -1, -1, -1,
- 23, 24, 66, 67, 68, 66, 67, 68, 31, 32,
- -1, -1, 35, 29, 37, -1, 15, 96, 97, 98,
- 36, -1, -1, -1, 96, 97, 98, 66, 67, 68,
- 29, -1, 96, 97, 98, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 15,
- 66, 67, 68, -1, -1, -1, 15, 96, 97, 98,
- 15, -1, -1, 29, 15, -1, 29, 66, 67, 68,
- 29, -1, -1, 36, 29, -1, -1, -1, 29, -1,
- 96, 97, 98, -1, -1, 29, -1, -1, 29, -1,
- -1, -1, -1, -1, -1, -1, -1, 96, 97, 98,
- 66, 67, 68, 66, 67, 68, -1, 66, 67, 68,
- -1, 66, 67, 68, -1, 66, 67, 68, -1, -1,
- 61, 62, 66, 67, 68, 66, 67, 68, 29, -1,
- 96, 97, 98, 96, 97, 98, -1, 96, 97, 98,
- 29, 96, 97, 98, -1, 96, 97, 98, 29, -1,
- -1, 29, 96, 97, 98, 96, 97, 98, -1, -1,
- 61, 62, -1, -1, 29, 66, 67, 68, -1, -1,
- -1, 36, 61, 62, -1, -1, -1, 66, 67, 68,
- 61, 62, -1, 61, 62, 66, 67, 68, 66, 67,
- 68, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- 3, 66, 67, 68, -1, -1, -1, 96, 97, 98,
- 13, -1, -1, -1, 17, 96, 97, 98, 96, 97,
- 98, -1, -1, 26, -1, 28, -1, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, 39, -1, 41, 42,
- -1, -1, -1, 29, -1, -1, 49, -1, -1, 52,
- 53, -1, -1, -1, -1, 58, -1, -1, -1, -1,
- -1, 64, -1, -1, -1, -1, -1, -1, -1, 3,
- -1, -1, -1, -1, -1, 61, 62, 80, -1, 13,
- 66, 67, 68, 17, -1, -1, -1, -1, -1, -1,
- -1, -1, 26, -1, 28, -1, -1, 31, -1, -1,
- -1, -1, -1, -1, -1, 39, -1, 41, 42, -1,
- 96, 97, 98, -1, -1, 49, -1, -1, 52, 53,
- -1, -1, 3, -1, 58, -1, -1, -1, -1, -1,
- 64, -1, 13, -1, -1, -1, 17, -1, -1, -1,
- -1, -1, -1, -1, -1, 26, 80, 28, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 39, -1,
- 41, 42, -1, -1, -1, -1, -1, -1, 49, -1,
- -1, 52, 53, -1, -1, -1, -1, 58, -1, -1,
- -1, -1, -1, 64, -1, 12, 13, -1, -1, -1,
- -1, -1, -1, -1, -1, 22, -1, -1, -1, 80,
- -1, -1, 29, -1, -1, -1, 33, 34, -1, 36,
- -1, -1, -1, -1, -1, -1, 43, -1, -1, -1,
- 47, -1, -1, -1, -1, -1, -1, 12, 13, -1,
- -1, -1, -1, -1, -1, -1, -1, 22, 65, 66,
- 67, 68, -1, 70, 29, -1, -1, -1, 33, 34,
- -1, 36, -1, -1, 81, 82, 83, -1, 43, -1,
- -1, 88, 47, -1, -1, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- 65, 66, 67, 68, -1, 70, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 81, 82, 83, -1,
- -1, -1, -1, 88, -1, -1, -1, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 12, 13, -1, -1, -1, -1, -1,
- -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
- 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
- -1, -1, -1, -1, 43, -1, -1, -1, 47, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 65, 66, 67, 68,
- -1, 70, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 81, 82, 83, -1, -1, -1, -1, 88,
- -1, -1, -1, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 12,
- 13, -1, -1, -1, -1, -1, -1, -1, -1, 22,
- -1, -1, -1, -1, -1, -1, 29, -1, -1, -1,
- 33, 34, -1, 36, -1, -1, -1, -1, -1, -1,
- 43, -1, -1, -1, 47, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 65, 66, 67, 68, -1, 70, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 81, 82,
- 83, -1, -1, -1, -1, 88, -1, -1, -1, -1,
- -1, -1, -1, 96, 97, 98, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 10, -1, 12, 13, -1,
- -1, -1, -1, -1, -1, -1, -1, 22, -1, -1,
- -1, -1, -1, -1, 29, -1, -1, -1, 33, 34,
- -1, 36, -1, -1, -1, -1, -1, -1, 43, -1,
- -1, -1, 47, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 65, 66, 67, 68, -1, 70, -1, -1, -1, -1,
- 75, -1, -1, -1, -1, -1, 81, 82, 83, 84,
- 85, -1, -1, 88, -1, -1, 91, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 10, -1, 12, 13, -1, -1, -1,
- -1, -1, -1, -1, -1, 22, -1, -1, -1, -1,
- -1, -1, 29, -1, -1, -1, 33, 34, -1, 36,
- -1, -1, -1, -1, -1, -1, 43, -1, -1, -1,
- 47, -1, -1, -1, -1, -1, -1, -1, 55, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 65, 66,
- 67, 68, -1, 70, -1, -1, -1, -1, 75, -1,
- -1, -1, -1, -1, 81, 82, 83, 84, 85, -1,
- -1, 88, -1, -1, 91, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 10, -1, 12, 13, -1, -1, -1, -1, -1,
- -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
- 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
- -1, -1, -1, -1, 43, -1, -1, -1, 47, -1,
- -1, -1, -1, -1, -1, -1, 55, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 65, 66, 67, 68,
- -1, 70, -1, -1, -1, -1, 75, -1, -1, -1,
- -1, -1, 81, 82, 83, 84, 85, -1, -1, 88,
- -1, -1, 91, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 11,
- 12, 13, -1, -1, -1, -1, -1, -1, -1, -1,
- 22, -1, -1, -1, -1, -1, -1, 29, -1, -1,
- -1, 33, 34, -1, 36, -1, -1, -1, 40, -1,
- 42, 43, 44, -1, -1, 47, -1, -1, -1, 51,
- -1, 53, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 65, 66, 67, 68, -1, 70, -1,
- 72, -1, 74, -1, 76, -1, -1, -1, -1, 81,
- 82, 83, -1, -1, -1, -1, 88, -1, -1, -1,
- -1, -1, -1, -1, 96, 97, 98, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 7, -1, -1, -1,
- 11, 12, 13, -1, -1, -1, -1, -1, -1, -1,
- -1, 22, -1, -1, -1, -1, -1, -1, 29, -1,
- -1, -1, 33, 34, -1, 36, -1, -1, -1, 40,
- -1, 42, 43, 44, -1, -1, 47, -1, -1, -1,
- 51, -1, 53, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 65, 66, 67, 68, -1, 70,
- -1, 72, -1, 74, -1, 76, -1, -1, -1, -1,
- 81, 82, 83, -1, -1, -1, -1, 88, -1, -1,
- -1, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, 75, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 11, 12, 13, -1, -1, -1,
- -1, -1, -1, -1, -1, 22, -1, -1, -1, -1,
- -1, -1, 29, 30, -1, -1, 33, 34, -1, 36,
- -1, -1, -1, 40, -1, 42, 43, 44, -1, -1,
- 47, -1, -1, -1, 51, -1, 53, -1, -1, -1,
- -1, -1, -1, -1, 61, -1, -1, -1, 65, 66,
- 67, 68, 69, 70, -1, 72, 73, 74, -1, 76,
- -1, 78, -1, -1, 81, 82, 83, -1, -1, -1,
- -1, 88, -1, -1, -1, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 11, 12, 13, -1, -1, -1, -1, -1, -1,
- -1, -1, 22, -1, -1, -1, -1, -1, -1, 29,
- 30, -1, -1, 33, 34, -1, 36, -1, -1, -1,
- 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
- -1, 51, -1, 53, -1, -1, -1, -1, -1, -1,
- -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
- 70, -1, 72, 73, 74, -1, 76, -1, 78, -1,
- -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
- -1, -1, -1, -1, -1, -1, 96, 97, 98, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 11, 12,
- 13, -1, -1, -1, -1, -1, -1, -1, -1, 22,
- -1, -1, -1, -1, -1, -1, 29, 30, -1, -1,
- 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
- 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
- 53, -1, -1, -1, -1, -1, -1, -1, 61, -1,
- -1, -1, 65, 66, 67, 68, 69, 70, -1, 72,
- 73, 74, -1, 76, -1, 78, -1, -1, 81, 82,
- 83, -1, -1, -1, -1, 88, -1, -1, -1, -1,
- -1, -1, -1, 96, 97, 98, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4, 5, 6, -1, -1,
- 9, 10, 11, -1, -1, 14, -1, 16, -1, -1,
- -1, 20, 21, 22, -1, -1, -1, -1, -1, -1,
- 29, 30, 31, 32, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 43, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 59, -1, -1, -1, -1, -1, -1, 66, 67, 68,
- 69, 70, 71, -1, 73, 74, 75, 76, 77, 78,
- -1, -1, 81, 82, 83, 84, 85, 86, 87, -1,
- -1, -1, 91, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
- 5, 6, -1, -1, 9, 10, 11, -1, -1, 14,
- -1, 16, -1, -1, -1, 20, 21, 22, -1, -1,
- -1, -1, -1, -1, 29, 30, 31, 32, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 43, -1,
- -1, -1, 47, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
- 65, 66, 67, 68, 69, 70, 71, -1, 73, 74,
- 75, 76, 77, 78, -1, -1, 81, 82, 83, 84,
- 85, 86, 87, -1, -1, -1, 91, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4, 5, 6, -1, -1, 9, 10,
- 11, -1, -1, 14, -1, 16, -1, -1, -1, 20,
- 21, 22, -1, -1, -1, -1, -1, -1, 29, 30,
- 31, 32, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 43, -1, -1, -1, 47, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 59, -1,
- -1, -1, -1, -1, 65, 66, 67, 68, 69, 70,
- 71, -1, 73, 74, 75, 76, 77, 78, -1, -1,
- 81, 82, 83, 84, 85, 86, 87, -1, -1, -1,
- 91, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4, 5, 6,
- -1, -1, 9, 10, 11, -1, -1, 14, -1, 16,
- -1, -1, -1, 20, 21, 22, -1, -1, -1, -1,
- -1, -1, 29, 30, 31, 32, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 43, -1, -1, -1,
- 47, -1, -1, -1, -1, -1, -1, -1, 55, -1,
- -1, -1, 59, -1, -1, -1, -1, -1, 65, 66,
- 67, 68, 69, 70, 71, -1, 73, 74, 75, 76,
- 77, 78, -1, -1, 81, 82, 83, 84, 85, 86,
- 87, -1, -1, -1, 91, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4, -1, -1, -1, -1, 9, -1, 11, 12,
- 13, 14, -1, -1, -1, -1, -1, -1, 21, 22,
- -1, -1, -1, -1, -1, -1, 29, 30, -1, -1,
- 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
- 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
- 53, -1, -1, -1, -1, -1, 59, -1, 61, -1,
- -1, -1, 65, 66, 67, 68, 69, 70, 71, 72,
- 73, 74, 75, 76, 77, 78, -1, -1, 81, 82,
- 83, 84, 85, 86, -1, 88, -1, -1, -1, -1,
- -1, -1, -1, 96, 97, 98, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4, -1, -1, -1, -1,
- 9, -1, 11, 12, 13, 14, -1, -1, -1, -1,
- -1, -1, 21, 22, -1, -1, -1, -1, -1, -1,
- 29, 30, -1, -1, 33, 34, -1, 36, -1, -1,
- -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
- -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
- 59, -1, 61, -1, -1, -1, 65, 66, 67, 68,
- 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
- -1, -1, 81, 82, 83, 84, 85, 86, -1, 88,
- -1, -1, -1, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
- 5, 6, -1, -1, 9, 10, 11, 12, 13, 14,
- -1, 16, -1, -1, -1, 20, 21, 22, -1, -1,
- -1, -1, -1, -1, 29, 30, 31, 32, 33, 34,
- -1, 36, -1, -1, -1, 40, -1, 42, 43, 44,
- -1, -1, 47, -1, -1, -1, 51, -1, 53, -1,
- -1, -1, -1, -1, 59, -1, 61, -1, -1, -1,
- 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
- 75, 76, 77, 78, -1, -1, 81, 82, 83, 84,
- 85, 86, 87, 88, -1, -1, 91, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4, 5, 6, -1, -1, 9, 10,
- 11, 12, 13, 14, -1, 16, -1, -1, -1, 20,
- 21, 22, -1, -1, -1, -1, -1, -1, 29, 30,
- 31, 32, 33, 34, -1, 36, -1, -1, -1, 40,
- -1, 42, 43, 44, -1, -1, 47, -1, -1, -1,
- 51, -1, 53, -1, 55, -1, -1, -1, 59, -1,
- 61, -1, -1, -1, 65, 66, 67, 68, 69, 70,
- 71, 72, 73, 74, 75, 76, 77, 78, -1, -1,
- 81, 82, 83, 84, 85, 86, 87, 88, -1, -1,
- 91, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1,
-
- 18, 18, 32, 18, 32, 18, 3, 43, 18, 25,
- 22, 22, 14, 14, 78, 18, 9, 3, 18, 3,
- 18, 3, 18, 32, 18, 32, 32, 22, 18, 18,
- 22, 3, 3, 18, 106, 43, 3, 18, 18, 101,
- 3, 3, 18, 18, 18, 104, 3, 3, 18, 18,
- 18, 3, 18, 18, 18, 43, 3, 3, 3, 32,
- 14, 3, 3, 3, 25, 25, 25, -1, 55, 18,
- 57, 2, 55, -1, -1, 2, 55, 60, 3, -1,
- 18, 60, 18, 18, -1, 43, 25, 18, 43, -1,
- 43, 18, 43, 43, 18, 43, 11, 12, 14, 43,
- 43, 4, 3, 19, 18, 18, 3, 14, 3, 45,
- 18, -1, 18, -1, 2, 18, 23, 2, 55, 2,
- 57, 55, 55, 57, 57, 55, 55, 57, 57, 55,
- 18, 57, 45, 18, 55, 18, 57, 2, 46, 2,
- 2, 47, 2, 55, 55, 55, 55, 57, 60, 60,
- 55, 60, 57, 18, 79, 18, 18, 55, 18, 55,
- 95, 57, 60, 14, 2, 2, 55, -1, 55, 18,
- 14, 55, 61, 55, 61, 19, 60, 59, 79, 55,
- 18, 18, 79, 59, 79, 55, 55, 55, 14, 55,
- 60, 60, 60, 18, 60, 2, 110, 55, 47, 55,
- 43, 52, 60, 59, 18, 55, 2, 57, 51, 55,
- 55, 18, 39, 55, 55, 18, 43, 4, 2, 55,
- 65, 67, 18, 2, 18, 61, 52, 2, 69, 71,
- 2, 18, 46, 55, 18, 57, 55, 4, 57, 18,
- 55, 44, 57, 18, 55, 55, 18, 58, 58, 18,
- 43, 18, 46, 55, 14, 57, 55, 18, 51, 19,
- 2, 2, 61, 55, 55, 57, 55, 55, 93, 57,
- -1, 60, 63, -1, -1, 55, 18, 18, 47, 68,
- 60, -1, 55, -1, 64, 46, 18, 60, -1, 62,
- 55, 55, -1, -1, -1, 60, 60, 62, 62, 55,
- 55, -1, 55, 55, 60, 60, 62, 60, 60, 55,
- 55, -1, 55, 66, 60, 60, 48, 60, 55, 55,
- -1, 14, 77, 60, 60, 77, 19, 72, 21, -1,
- 14, 77, -1, 70, 77, 5, -1, 5, -1, 23,
- -1, 77, -1, -1, 14, -1, 14, 89, 89, -1,
- 43, 35, 36, 23, -1, 23, 25, 26, 27, 28,
- 29, 30, 31, -1, -1, 35, 36, 35, 36, -1,
- -1, -1, 14, -1, -1, -1, -1, -1, -1, -1,
- 18, 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 14, -1, -1, -1, -1, -1, -1, -1, -1, 23,
- 24, 25, 26, 27, 28, 29, 30, 31, -1, -1,
- 48, 49, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 14, -1, -1, -1, -1, -1,
- -1, -1, -1, 23, 24, 25, 26, 27, 28, 29,
- 30, 31, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1
-};
-
-QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsgrammar_p.h b/src/qml/parser/qqmljsgrammar_p.h
deleted file mode 100644
index 9d028f2191..0000000000
--- a/src/qml/parser/qqmljsgrammar_p.h
+++ /dev/null
@@ -1,215 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-// This file was generated by qlalr - DO NOT EDIT!
-#ifndef QQMLJSGRAMMAR_P_H
-#define QQMLJSGRAMMAR_P_H
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlJSGrammar
-{
-public:
- enum VariousConstants {
- EOF_SYMBOL = 0,
- REDUCE_HERE = 107,
- SHIFT_THERE = 106,
- T_AND = 1,
- T_AND_AND = 2,
- T_AND_EQ = 3,
- T_AS = 95,
- T_AUTOMATIC_SEMICOLON = 62,
- T_BREAK = 4,
- T_CASE = 5,
- T_CATCH = 6,
- T_COLON = 7,
- T_COMMA = 8,
- T_COMMENT = 89,
- T_COMPATIBILITY_SEMICOLON = 90,
- T_CONST = 84,
- T_CONTINUE = 9,
- T_DEBUGGER = 86,
- T_DEFAULT = 10,
- T_DELETE = 11,
- T_DIVIDE_ = 12,
- T_DIVIDE_EQ = 13,
- T_DO = 14,
- T_DOT = 15,
- T_ELSE = 16,
- T_ENUM = 91,
- T_EQ = 17,
- T_EQ_EQ = 18,
- T_EQ_EQ_EQ = 19,
- T_ERROR = 99,
- T_FALSE = 83,
- T_FEED_JS_EXPRESSION = 103,
- T_FEED_JS_PROGRAM = 105,
- T_FEED_JS_SOURCE_ELEMENT = 104,
- T_FEED_JS_STATEMENT = 102,
- T_FEED_UI_OBJECT_MEMBER = 101,
- T_FEED_UI_PROGRAM = 100,
- T_FINALLY = 20,
- T_FOR = 21,
- T_FUNCTION = 22,
- T_GE = 23,
- T_GET = 97,
- T_GT = 24,
- T_GT_GT = 25,
- T_GT_GT_EQ = 26,
- T_GT_GT_GT = 27,
- T_GT_GT_GT_EQ = 28,
- T_IDENTIFIER = 29,
- T_IF = 30,
- T_IMPORT = 93,
- T_IN = 31,
- T_INSTANCEOF = 32,
- T_LBRACE = 33,
- T_LBRACKET = 34,
- T_LE = 35,
- T_LET = 85,
- T_LPAREN = 36,
- T_LT = 37,
- T_LT_LT = 38,
- T_LT_LT_EQ = 39,
- T_MINUS = 40,
- T_MINUS_EQ = 41,
- T_MINUS_MINUS = 42,
- T_MULTILINE_STRING_LITERAL = 88,
- T_NEW = 43,
- T_NOT = 44,
- T_NOT_EQ = 45,
- T_NOT_EQ_EQ = 46,
- T_NULL = 81,
- T_NUMERIC_LITERAL = 47,
- T_ON = 96,
- T_OR = 48,
- T_OR_EQ = 49,
- T_OR_OR = 50,
- T_PLUS = 51,
- T_PLUS_EQ = 52,
- T_PLUS_PLUS = 53,
- T_PRAGMA = 94,
- T_PROPERTY = 66,
- T_PUBLIC = 92,
- T_QUESTION = 54,
- T_RBRACE = 55,
- T_RBRACKET = 56,
- T_READONLY = 68,
- T_REMAINDER = 57,
- T_REMAINDER_EQ = 58,
- T_RESERVED_WORD = 87,
- T_RETURN = 59,
- T_RPAREN = 60,
- T_SEMICOLON = 61,
- T_SET = 98,
- T_SIGNAL = 67,
- T_STAR = 63,
- T_STAR_EQ = 64,
- T_STRING_LITERAL = 65,
- T_SWITCH = 69,
- T_THIS = 70,
- T_THROW = 71,
- T_TILDE = 72,
- T_TRUE = 82,
- T_TRY = 73,
- T_TYPEOF = 74,
- T_VAR = 75,
- T_VOID = 76,
- T_WHILE = 77,
- T_WITH = 78,
- T_XOR = 79,
- T_XOR_EQ = 80,
-
- ACCEPT_STATE = 691,
- RULE_COUNT = 369,
- STATE_COUNT = 692,
- TERMINAL_COUNT = 108,
- NON_TERMINAL_COUNT = 112,
-
- GOTO_INDEX_OFFSET = 692,
- GOTO_INFO_OFFSET = 3357,
- GOTO_CHECK_OFFSET = 3357
- };
-
- static const char *const spell[];
- static const short lhs[];
- static const short rhs[];
- static const short goto_default[];
- static const short action_default[];
- static const short action_index[];
- static const short action_info[];
- static const short action_check[];
-
- static inline int nt_action (int state, int nt)
- {
- const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt;
- if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt)
- return goto_default [nt];
-
- return action_info [GOTO_INFO_OFFSET + yyn];
- }
-
- static inline int t_action (int state, int token)
- {
- const int yyn = action_index [state] + token;
-
- if (yyn < 0 || action_check [yyn] != token)
- return - action_default [state];
-
- return action_info [yyn];
- }
-};
-
-
-QT_END_NAMESPACE
-#endif // QQMLJSGRAMMAR_P_H
-
diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h
index 20daf545a9..40094ecdf8 100644
--- a/src/qml/parser/qqmljskeywords_p.h
+++ b/src/qml/parser/qqmljskeywords_p.h
@@ -57,10 +57,10 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
-static inline int classify2(const QChar *s, bool qmlMode) {
+static inline int classify2(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'a') {
if (s[1].unicode() == 's') {
- return qmlMode ? Lexer::T_AS : Lexer::T_IDENTIFIER;
+ return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_AS : Lexer::T_IDENTIFIER;
}
}
else if (s[0].unicode() == 'd') {
@@ -76,15 +76,18 @@ static inline int classify2(const QChar *s, bool qmlMode) {
return Lexer::T_IN;
}
}
- else if (qmlMode && s[0].unicode() == 'o') {
+ else if (s[0].unicode() == 'o') {
if (s[1].unicode() == 'n') {
- return qmlMode ? Lexer::T_ON : Lexer::T_IDENTIFIER;
+ return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_ON : Lexer::T_IDENTIFIER;
+ }
+ else if (s[1].unicode() == 'f') {
+ return Lexer::T_OF;
}
}
return Lexer::T_IDENTIFIER;
}
-static inline int classify3(const QChar *s, bool qmlMode) {
+static inline int classify3(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'f') {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 'r') {
@@ -102,7 +105,7 @@ static inline int classify3(const QChar *s, bool qmlMode) {
else if (s[0].unicode() == 'i') {
if (s[1].unicode() == 'n') {
if (s[2].unicode() == 't') {
- return qmlMode ? int(Lexer::T_INT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_INT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -144,12 +147,12 @@ static inline int classify3(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify4(const QChar *s, bool qmlMode) {
+static inline int classify4(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'b') {
if (s[1].unicode() == 'y') {
if (s[2].unicode() == 't') {
if (s[3].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_BYTE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_BYTE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -165,7 +168,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
else if (s[1].unicode() == 'h') {
if (s[2].unicode() == 'a') {
if (s[3].unicode() == 'r') {
- return qmlMode ? int(Lexer::T_CHAR) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_CHAR) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -181,7 +184,16 @@ static inline int classify4(const QChar *s, bool qmlMode) {
else if (s[1].unicode() == 'n') {
if (s[2].unicode() == 'u') {
if (s[3].unicode() == 'm') {
- return qmlMode ? int(Lexer::T_ENUM) : int(Lexer::T_RESERVED_WORD);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_ENUM) : int(Lexer::T_RESERVED_WORD);
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'f') {
+ if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'o') {
+ if (s[3].unicode() == 'm') {
+ return int(Lexer::T_FROM);
}
}
}
@@ -190,7 +202,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 't') {
if (s[3].unicode() == 'o') {
- return qmlMode ? int(Lexer::T_GOTO) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_GOTO) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -199,7 +211,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 'n') {
if (s[3].unicode() == 'g') {
- return qmlMode ? int(Lexer::T_LONG) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_LONG) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -250,7 +262,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify5(const QChar *s, bool qmlMode) {
+static inline int classify5(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'b') {
if (s[1].unicode() == 'r') {
if (s[2].unicode() == 'e') {
@@ -305,7 +317,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'n') {
if (s[3].unicode() == 'a') {
if (s[4].unicode() == 'l') {
- return qmlMode ? int(Lexer::T_FINAL) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_FINAL) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -314,7 +326,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'o') {
if (s[3].unicode() == 'a') {
if (s[4].unicode() == 't') {
- return qmlMode ? int(Lexer::T_FLOAT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_FLOAT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -325,7 +337,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'o') {
if (s[3].unicode() == 'r') {
if (s[4].unicode() == 't') {
- return qmlMode ? int(Lexer::T_SHORT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_SHORT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -334,7 +346,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'p') {
if (s[3].unicode() == 'e') {
if (s[4].unicode() == 'r') {
- return qmlMode ? int(Lexer::T_SUPER) : int(Lexer::T_RESERVED_WORD);
+ return int(Lexer::T_SUPER);
}
}
}
@@ -362,10 +374,21 @@ static inline int classify5(const QChar *s, bool qmlMode) {
}
}
}
+ else if (s[0].unicode() == 'y') {
+ if (s[1].unicode() == 'i') {
+ if (s[2].unicode() == 'e') {
+ if (s[3].unicode() == 'l') {
+ if (s[4].unicode() == 'd') {
+ return (parseModeFlags & Lexer::YieldIsKeyword) ? Lexer::T_YIELD : Lexer::T_IDENTIFIER;
+ }
+ }
+ }
+ }
+ }
return Lexer::T_IDENTIFIER;
}
-static inline int classify6(const QChar *s, bool qmlMode) {
+static inline int classify6(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'd') {
if (s[1].unicode() == 'e') {
if (s[2].unicode() == 'l') {
@@ -383,7 +406,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'b') {
if (s[4].unicode() == 'l') {
if (s[5].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_DOUBLE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_DOUBLE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -409,7 +432,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'o') {
if (s[4].unicode() == 'r') {
if (s[5].unicode() == 't') {
- return qmlMode ? int(Lexer::T_IMPORT) : int(Lexer::T_RESERVED_WORD);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_IMPORT) : int(Lexer::T_RESERVED_WORD);
}
}
}
@@ -422,7 +445,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'i') {
if (s[4].unicode() == 'v') {
if (s[5].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_NATIVE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_NATIVE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -435,7 +458,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'l') {
if (s[4].unicode() == 'i') {
if (s[5].unicode() == 'c') {
- return qmlMode ? Lexer::T_PUBLIC : Lexer::T_IDENTIFIER;
+ return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_PUBLIC : Lexer::T_IDENTIFIER;
}
}
}
@@ -446,7 +469,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'g') {
if (s[4].unicode() == 'm') {
if (s[5].unicode() == 'a') {
- return qmlMode ? Lexer::T_PRAGMA : Lexer::T_IDENTIFIER;
+ return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_PRAGMA : Lexer::T_IDENTIFIER;
}
}
}
@@ -467,7 +490,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
}
}
else if (s[0].unicode() == 's') {
- if (qmlMode && s[1].unicode() == 'i') {
+ if ((parseModeFlags & Lexer::QmlMode) && s[1].unicode() == 'i') {
if (s[2].unicode() == 'g') {
if (s[3].unicode() == 'n') {
if (s[4].unicode() == 'a') {
@@ -483,7 +506,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 't') {
if (s[4].unicode() == 'i') {
if (s[5].unicode() == 'c') {
- return qmlMode ? int(Lexer::T_STATIC) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::StaticIsKeyword) ? int(Lexer::T_STATIC) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -507,7 +530,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'o') {
if (s[4].unicode() == 'w') {
if (s[5].unicode() == 's') {
- return qmlMode ? int(Lexer::T_THROWS) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_THROWS) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -528,7 +551,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify7(const QChar *s, bool qmlMode) {
+static inline int classify7(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'b') {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 'o') {
@@ -536,7 +559,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
if (s[4].unicode() == 'e') {
if (s[5].unicode() == 'a') {
if (s[6].unicode() == 'n') {
- return qmlMode ? int(Lexer::T_BOOLEAN) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_BOOLEAN) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -596,7 +619,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
if (s[4].unicode() == 'a') {
if (s[5].unicode() == 'g') {
if (s[6].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_PACKAGE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PACKAGE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -609,7 +632,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
if (s[4].unicode() == 'a') {
if (s[5].unicode() == 't') {
if (s[6].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_PRIVATE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PRIVATE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -620,7 +643,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify8(const QChar *s, bool qmlMode) {
+static inline int classify8(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'a') {
if (s[1].unicode() == 'b') {
if (s[2].unicode() == 's') {
@@ -629,7 +652,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
if (s[5].unicode() == 'a') {
if (s[6].unicode() == 'c') {
if (s[7].unicode() == 't') {
- return qmlMode ? int(Lexer::T_ABSTRACT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_ABSTRACT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -689,7 +712,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
}
}
}
- else if (qmlMode && s[0].unicode() == 'p') {
+ else if ((parseModeFlags & Lexer::QmlMode) && s[0].unicode() == 'p') {
if (s[1].unicode() == 'r') {
if (s[2].unicode() == 'o') {
if (s[3].unicode() == 'p') {
@@ -706,7 +729,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
}
}
}
- else if (qmlMode && s[0].unicode() == 'r') {
+ else if ((parseModeFlags & Lexer::QmlMode) && s[0].unicode() == 'r') {
if (s[1].unicode() == 'e') {
if (s[2].unicode() == 'a') {
if (s[3].unicode() == 'd') {
@@ -731,7 +754,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
if (s[5].unicode() == 'i') {
if (s[6].unicode() == 'l') {
if (s[7].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_VOLATILE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_VOLATILE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -743,7 +766,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify9(const QChar *s, bool qmlMode) {
+static inline int classify9(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'i') {
if (s[1].unicode() == 'n') {
if (s[2].unicode() == 't') {
@@ -753,7 +776,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
if (s[6].unicode() == 'a') {
if (s[7].unicode() == 'c') {
if (s[8].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_INTERFACE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_INTERFACE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -772,7 +795,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
if (s[6].unicode() == 't') {
if (s[7].unicode() == 'e') {
if (s[8].unicode() == 'd') {
- return qmlMode ? int(Lexer::T_PROTECTED) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PROTECTED) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -791,7 +814,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
if (s[6].unicode() == 'e') {
if (s[7].unicode() == 'n') {
if (s[8].unicode() == 't') {
- return qmlMode ? int(Lexer::T_TRANSIENT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_TRANSIENT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -804,7 +827,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify10(const QChar *s, bool qmlMode) {
+static inline int classify10(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'i') {
if (s[1].unicode() == 'm') {
if (s[2].unicode() == 'p') {
@@ -815,7 +838,7 @@ static inline int classify10(const QChar *s, bool qmlMode) {
if (s[7].unicode() == 'n') {
if (s[8].unicode() == 't') {
if (s[9].unicode() == 's') {
- return qmlMode ? int(Lexer::T_IMPLEMENTS) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_IMPLEMENTS) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -848,7 +871,7 @@ static inline int classify10(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify12(const QChar *s, bool qmlMode) {
+static inline int classify12(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 's') {
if (s[1].unicode() == 'y') {
if (s[2].unicode() == 'n') {
@@ -861,7 +884,7 @@ static inline int classify12(const QChar *s, bool qmlMode) {
if (s[9].unicode() == 'z') {
if (s[10].unicode() == 'e') {
if (s[11].unicode() == 'd') {
- return qmlMode ? int(Lexer::T_SYNCHRONIZED) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_SYNCHRONIZED) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -877,18 +900,18 @@ static inline int classify12(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-int Lexer::classify(const QChar *s, int n, bool qmlMode) {
+int Lexer::classify(const QChar *s, int n, int parseModeFlags) {
switch (n) {
- case 2: return classify2(s, qmlMode);
- case 3: return classify3(s, qmlMode);
- case 4: return classify4(s, qmlMode);
- case 5: return classify5(s, qmlMode);
- case 6: return classify6(s, qmlMode);
- case 7: return classify7(s, qmlMode);
- case 8: return classify8(s, qmlMode);
- case 9: return classify9(s, qmlMode);
- case 10: return classify10(s, qmlMode);
- case 12: return classify12(s, qmlMode);
+ case 2: return classify2(s, parseModeFlags);
+ case 3: return classify3(s, parseModeFlags);
+ case 4: return classify4(s, parseModeFlags);
+ case 5: return classify5(s, parseModeFlags);
+ case 6: return classify6(s, parseModeFlags);
+ case 7: return classify7(s, parseModeFlags);
+ case 8: return classify8(s, parseModeFlags);
+ case 9: return classify9(s, parseModeFlags);
+ case 10: return classify10(s, parseModeFlags);
+ case 12: return classify12(s, parseModeFlags);
default: return Lexer::T_IDENTIFIER;
} // switch
}
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index aab9025a08..ae016076b8 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -77,22 +77,15 @@ static inline QChar convertHex(QChar c1, QChar c2)
return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
}
-static inline QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4)
-{
- return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()),
- (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
-}
-
Lexer::Lexer(Engine *engine)
: _engine(engine)
, _codePtr(nullptr)
, _endPtr(nullptr)
- , _lastLinePtr(nullptr)
- , _tokenLinePtr(nullptr)
, _tokenStartPtr(nullptr)
, _char(QLatin1Char('\n'))
, _errorCode(NoError)
, _currentLineNumber(0)
+ , _currentColumnNumber(0)
, _tokenValue(0)
, _parenthesesState(IgnoreParentheses)
, _parenthesesCount(0)
@@ -101,6 +94,7 @@ Lexer::Lexer(Engine *engine)
, _tokenKind(0)
, _tokenLength(0)
, _tokenLine(0)
+ , _tokenColumn(0)
, _validTokenText(false)
, _prohibitAutomaticSemicolon(false)
, _restrictedKeyword(false)
@@ -137,14 +131,13 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
_codePtr = code.unicode();
_endPtr = _codePtr + code.length();
- _lastLinePtr = _codePtr;
- _tokenLinePtr = _codePtr;
_tokenStartPtr = _codePtr;
_char = QLatin1Char('\n');
_errorCode = NoError;
_currentLineNumber = lineno;
+ _currentColumnNumber = 0;
_tokenValue = 0;
// parentheses state
@@ -156,6 +149,7 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
_patternFlags = 0;
_tokenLength = 0;
_tokenLine = lineno;
+ _tokenColumn = 0;
_validTokenText = false;
_prohibitAutomaticSemicolon = false;
@@ -172,9 +166,10 @@ void Lexer::scanChar()
if (sequenceLength == 2)
_char = *_codePtr++;
- if (unsigned sequenceLength = isLineTerminatorSequence()) {
- _lastLinePtr = _codePtr + sequenceLength - 1; // points to the first character after the newline
+ ++_currentColumnNumber;
+ if (isLineTerminator()) {
++_currentLineNumber;
+ _currentColumnNumber = 0;
}
}
@@ -222,12 +217,32 @@ inline bool isBinop(int tok)
return false;
}
}
+
+int hexDigit(QChar c)
+{
+ if (c >= QLatin1Char('0') && c <= QLatin1Char('9'))
+ return c.unicode() - '0';
+ if (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
+ return c.unicode() - 'a' + 10;
+ if (c >= QLatin1Char('A') && c <= QLatin1Char('F'))
+ return c.unicode() - 'A' + 10;
+ return -1;
+}
+
+int octalDigit(QChar c)
+{
+ if (c >= QLatin1Char('0') && c <= QLatin1Char('7'))
+ return c.unicode() - '0';
+ return -1;
+}
+
} // anonymous namespace
int Lexer::lex()
{
const int previousTokenKind = _tokenKind;
+ again:
_tokenSpell = QStringRef();
_tokenKind = scanToken();
_tokenLength = _codePtr - _tokenStartPtr - 1;
@@ -239,6 +254,9 @@ int Lexer::lex()
// update the flags
switch (_tokenKind) {
case T_LBRACE:
+ if (_bracesCount > 0)
+ ++_bracesCount;
+ Q_FALLTHROUGH();
case T_SEMICOLON:
case T_QUESTION:
case T_COLON:
@@ -266,9 +284,15 @@ int Lexer::lex()
case T_CONTINUE:
case T_BREAK:
case T_RETURN:
+ case T_YIELD:
case T_THROW:
_restrictedKeyword = true;
break;
+ case T_RBRACE:
+ if (_bracesCount > 0)
+ --_bracesCount;
+ if (_bracesCount == 0)
+ goto again;
} // switch
// update the parentheses state
@@ -295,39 +319,57 @@ int Lexer::lex()
return _tokenKind;
}
-bool Lexer::isUnicodeEscapeSequence(const QChar *chars)
-{
- if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3]))
- return true;
-
- return false;
-}
-
-QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok)
+uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
{
- if (_char == QLatin1Char('u') && isUnicodeEscapeSequence(&_codePtr[0])) {
- scanChar(); // skip u
+ Q_ASSERT(_char == QLatin1Char('u'));
+ scanChar(); // skip u
+ if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) {
+ uint codePoint = 0;
+ for (int i = 0; i < 4; ++i) {
+ int digit = hexDigit(_char);
+ if (digit < 0)
+ goto error;
+ codePoint *= 16;
+ codePoint += digit;
+ scanChar();
+ }
- const QChar c1 = _char;
- scanChar();
+ *ok = true;
+ return codePoint;
+ } else if (_codePtr < _endPtr && _char == QLatin1Char('{')) {
+ scanChar(); // skip '{'
+ uint codePoint = 0;
+ if (!isHexDigit(_char))
+ // need at least one hex digit
+ goto error;
- const QChar c2 = _char;
- scanChar();
+ while (_codePtr <= _endPtr) {
+ int digit = hexDigit(_char);
+ if (digit < 0)
+ break;
+ codePoint *= 16;
+ codePoint += digit;
+ if (codePoint > 0x10ffff)
+ goto error;
+ scanChar();
+ }
- const QChar c3 = _char;
- scanChar();
+ if (_char != QLatin1Char('}'))
+ goto error;
- const QChar c4 = _char;
- scanChar();
+ scanChar(); // skip '}'
- if (ok)
- *ok = true;
- return convertUnicode(c1, c2, c3, c4);
+ *ok = true;
+ return codePoint;
}
+ error:
+ _errorCode = IllegalUnicodeEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+
*ok = false;
- return QChar();
+ return 0;
}
QChar Lexer::decodeHexEscapeCharacter(bool *ok)
@@ -351,15 +393,15 @@ QChar Lexer::decodeHexEscapeCharacter(bool *ok)
return QChar();
}
-static inline bool isIdentifierStart(QChar ch)
+static inline bool isIdentifierStart(uint ch)
{
// fast path for ascii
- if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') ||
- (ch.unicode() >= 'A' && ch.unicode() <= 'Z') ||
+ if ((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
ch == '$' || ch == '_')
return true;
- switch (ch.category()) {
+ switch (QChar::category(ch)) {
case QChar::Number_Letter:
case QChar::Letter_Uppercase:
case QChar::Letter_Lowercase:
@@ -373,17 +415,17 @@ static inline bool isIdentifierStart(QChar ch)
return false;
}
-static bool isIdentifierPart(QChar ch)
+static bool isIdentifierPart(uint ch)
{
// fast path for ascii
- if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') ||
- (ch.unicode() >= 'A' && ch.unicode() <= 'Z') ||
- (ch.unicode() >= '0' && ch.unicode() <= '9') ||
+ if ((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
ch == '$' || ch == '_' ||
- ch.unicode() == 0x200c /* ZWNJ */ || ch.unicode() == 0x200d /* ZWJ */)
+ ch == 0x200c /* ZWNJ */ || ch == 0x200d /* ZWJ */)
return true;
- switch (ch.category()) {
+ switch (QChar::category(ch)) {
case QChar::Mark_NonSpacing:
case QChar::Mark_SpacingCombining:
@@ -412,19 +454,23 @@ int Lexer::scanToken()
return tk;
}
+ if (_bracesCount == 0) {
+ // we're inside a Template string
+ return scanString(TemplateContinuation);
+ }
+
+
_terminator = false;
again:
_validTokenText = false;
- _tokenLinePtr = _lastLinePtr;
while (_char.isSpace()) {
- if (unsigned sequenceLength = isLineTerminatorSequence()) {
- _tokenLinePtr = _codePtr + sequenceLength - 1;
-
+ if (isLineTerminator()) {
if (_restrictedKeyword) {
// automatic semicolon insertion
_tokenLine = _currentLineNumber;
+ _tokenColumn = _currentColumnNumber;
_tokenStartPtr = _codePtr - 1;
return T_SEMICOLON;
} else {
@@ -438,6 +484,7 @@ again:
_tokenStartPtr = _codePtr - 1;
_tokenLine = _currentLineNumber;
+ _tokenColumn = _currentColumnNumber;
if (_codePtr > _endPtr)
return EOF_SYMBOL;
@@ -501,6 +548,9 @@ again:
return T_EQ_EQ_EQ;
}
return T_EQ_EQ;
+ } else if (_char == QLatin1Char('>')) {
+ scanChar();
+ return T_ARROW;
}
return T_EQ;
@@ -557,50 +607,18 @@ again:
return T_DIVIDE_;
case '.':
- if (_char.isDigit()) {
- QVarLengthArray<char,32> chars;
-
- chars.append(ch.unicode()); // append the `.'
-
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ if (isDecimalDigit(_char.unicode()))
+ return scanNumber(ch);
+ if (_char == QLatin1Char('.')) {
+ scanChar();
+ if (_char == QLatin1Char('.')) {
scanChar();
- }
-
- if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
- if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
- _codePtr[1].isDigit())) {
-
- chars.append(_char.unicode());
- scanChar(); // consume `e'
-
- if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
- chars.append(_char.unicode());
- scanChar(); // consume the sign
- }
-
- while (_char.isDigit()) {
- chars.append(_char.unicode());
- scanChar();
- }
- }
- }
-
- chars.append('\0');
-
- const char *begin = chars.constData();
- const char *end = nullptr;
- bool ok = false;
-
- _tokenValue = qstrtod(begin, &end, &ok);
-
- if (end - begin != chars.size() - 1) {
- _errorCode = IllegalExponentIndicator;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number");
+ return T_ELLIPSIS;
+ } else {
+ _errorCode = IllegalCharacter;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Unexpected token '.'");
return T_ERROR;
}
-
- return T_NUMERIC_LITERAL;
}
return T_DOT;
@@ -611,7 +629,7 @@ again:
} else if (_char == QLatin1Char('-')) {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) {
+ if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
_stackToken = T_MINUS_MINUS;
return T_SEMICOLON;
}
@@ -629,7 +647,7 @@ again:
} else if (_char == QLatin1Char('+')) {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) {
+ if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
_stackToken = T_PLUS_PLUS;
return T_SEMICOLON;
}
@@ -642,6 +660,13 @@ again:
if (_char == QLatin1Char('=')) {
scanChar();
return T_STAR_EQ;
+ } else if (_char == QLatin1Char('*')) {
+ scanChar();
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_STAR_STAR_EQ;
+ }
+ return T_STAR_STAR;
}
return T_STAR;
@@ -676,141 +701,12 @@ again:
}
return T_NOT;
+ case '`':
+ _outerTemplateBraceCount.push(_bracesCount);
+ Q_FALLTHROUGH();
case '\'':
- case '"': {
- const QChar quote = ch;
- bool multilineStringLiteral = false;
-
- const QChar *startCode = _codePtr;
-
- if (_engine) {
- while (_codePtr <= _endPtr) {
- if (isLineTerminator()) {
- if (qmlMode())
- break;
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
- return T_ERROR;
- } else if (_char == QLatin1Char('\\')) {
- break;
- } else if (_char == quote) {
- _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
- scanChar();
-
- return T_STRING_LITERAL;
- }
- scanChar();
- }
- }
-
- _validTokenText = true;
- _tokenText.resize(0);
- startCode--;
- while (startCode != _codePtr - 1)
- _tokenText += *startCode++;
-
- while (_codePtr <= _endPtr) {
- if (unsigned sequenceLength = isLineTerminatorSequence()) {
- multilineStringLiteral = true;
- _tokenText += _char;
- if (sequenceLength == 2)
- _tokenText += *_codePtr;
- scanChar();
- } else if (_char == quote) {
- scanChar();
-
- if (_engine)
- _tokenSpell = _engine->newStringRef(_tokenText);
-
- return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
- } else if (_char == QLatin1Char('\\')) {
- scanChar();
- if (_codePtr > _endPtr) {
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
- return T_ERROR;
- }
-
- QChar u;
-
- switch (_char.unicode()) {
- // unicode escape sequence
- case 'u': {
- bool ok = false;
- u = decodeUnicodeEscapeCharacter(&ok);
- if (! ok) {
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
- return T_ERROR;
- }
- } break;
-
- // hex escape sequence
- case 'x': {
- bool ok = false;
- u = decodeHexEscapeCharacter(&ok);
- if (!ok) {
- _errorCode = IllegalHexadecimalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
- return T_ERROR;
- }
- } break;
-
- // single character escape sequence
- case '\\': u = QLatin1Char('\\'); scanChar(); break;
- case '\'': u = QLatin1Char('\''); scanChar(); break;
- case '\"': u = QLatin1Char('\"'); scanChar(); break;
- case 'b': u = QLatin1Char('\b'); scanChar(); break;
- case 'f': u = QLatin1Char('\f'); scanChar(); break;
- case 'n': u = QLatin1Char('\n'); scanChar(); break;
- case 'r': u = QLatin1Char('\r'); scanChar(); break;
- case 't': u = QLatin1Char('\t'); scanChar(); break;
- case 'v': u = QLatin1Char('\v'); scanChar(); break;
-
- case '0':
- if (! _codePtr->isDigit()) {
- scanChar();
- u = QLatin1Char('\0');
- break;
- }
- Q_FALLTHROUGH();
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
- return T_ERROR;
-
- case '\r':
- case '\n':
- case 0x2028u:
- case 0x2029u:
- scanChar();
- continue;
-
- default:
- // non escape character
- u = _char;
- scanChar();
- }
-
- _tokenText += u;
- } else {
- _tokenText += _char;
- scanChar();
- }
- }
-
- _errorCode = UnclosedStringLiteral;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
- return T_ERROR;
- }
+ case '"':
+ return scanString(ScanStringMode(ch.unicode()));
case '0':
case '1':
case '2':
@@ -824,28 +720,36 @@ again:
return scanNumber(ch);
default: {
- QChar c = ch;
+ uint c = ch.unicode();
bool identifierWithEscapeChars = false;
- if (c == QLatin1Char('\\') && _char == QLatin1Char('u')) {
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) {
+ c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
+ scanChar();
+ } else if (c == '\\' && _char == QLatin1Char('u')) {
identifierWithEscapeChars = true;
bool ok = false;
c = decodeUnicodeEscapeCharacter(&ok);
- if (! ok) {
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+ if (!ok)
return T_ERROR;
- }
}
if (isIdentifierStart(c)) {
if (identifierWithEscapeChars) {
_tokenText.resize(0);
- _tokenText += c;
+ if (QChar::requiresSurrogates(c)) {
+ _tokenText += QChar(QChar::highSurrogate(c));
+ _tokenText += QChar(QChar::lowSurrogate(c));
+ } else {
+ _tokenText += QChar(c);
+ }
_validTokenText = true;
}
- while (true) {
- c = _char;
- if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) {
- if (! identifierWithEscapeChars) {
+ while (_codePtr <= _endPtr) {
+ c = _char.unicode();
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) {
+ scanChar();
+ c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
+ } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) {
+ if (!identifierWithEscapeChars) {
identifierWithEscapeChars = true;
_tokenText.resize(0);
_tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1);
@@ -855,38 +759,52 @@ again:
scanChar(); // skip '\\'
bool ok = false;
c = decodeUnicodeEscapeCharacter(&ok);
- if (! ok) {
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+ if (!ok)
return T_ERROR;
- }
- if (isIdentifierPart(c))
- _tokenText += c;
- continue;
- } else if (isIdentifierPart(c)) {
- if (identifierWithEscapeChars)
- _tokenText += c;
- scanChar();
+ if (!isIdentifierPart(c))
+ break;
+
+ if (identifierWithEscapeChars) {
+ if (QChar::requiresSurrogates(c)) {
+ _tokenText += QChar(QChar::highSurrogate(c));
+ _tokenText += QChar(QChar::lowSurrogate(c));
+ } else {
+ _tokenText += QChar(c);
+ }
+ }
continue;
}
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ if (!isIdentifierPart(c))
+ break;
- int kind = T_IDENTIFIER;
+ if (identifierWithEscapeChars) {
+ if (QChar::requiresSurrogates(c)) {
+ _tokenText += QChar(QChar::highSurrogate(c));
+ _tokenText += QChar(QChar::lowSurrogate(c));
+ } else {
+ _tokenText += QChar(c);
+ }
+ }
+ scanChar();
+ }
+
+ _tokenLength = _codePtr - _tokenStartPtr - 1;
- if (! identifierWithEscapeChars)
- kind = classify(_tokenStartPtr, _tokenLength, _qmlMode);
+ int kind = T_IDENTIFIER;
- if (_engine) {
- if (kind == T_IDENTIFIER && identifierWithEscapeChars)
- _tokenSpell = _engine->newStringRef(_tokenText);
- else
- _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
- }
+ if (!identifierWithEscapeChars)
+ kind = classify(_tokenStartPtr, _tokenLength, parseModeFlags());
- return kind;
+ if (_engine) {
+ if (kind == T_IDENTIFIER && identifierWithEscapeChars)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+ else
+ _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
}
+
+ return kind;
}
}
@@ -896,93 +814,278 @@ again:
return T_ERROR;
}
-int Lexer::scanNumber(QChar ch)
+int Lexer::scanString(ScanStringMode mode)
{
- if (ch != QLatin1Char('0')) {
- QVarLengthArray<char, 64> buf;
- buf += ch.toLatin1();
-
- QChar n = _char;
- const QChar *code = _codePtr;
- while (n.isDigit()) {
- buf += n.toLatin1();
- n = *code++;
- }
+ QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode);
+ bool multilineStringLiteral = false;
- if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) {
- if (code != _codePtr) {
- _codePtr = code - 1;
+ const QChar *startCode = _codePtr;
+
+ if (_engine) {
+ while (_codePtr <= _endPtr) {
+ if (isLineTerminator() && quote != QLatin1Char('`')) {
+ if (qmlMode())
+ break;
+ _errorCode = IllegalCharacter;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
+ return T_ERROR;
+ } else if (_char == QLatin1Char('\\')) {
+ break;
+ } else if (_char == '$' && quote == QLatin1Char('`')) {
+ break;
+ } else if (_char == quote) {
+ _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
scanChar();
+
+ if (quote == QLatin1Char('`'))
+ _bracesCount = _outerTemplateBraceCount.pop();
+
+ if (mode == TemplateHead)
+ return T_NO_SUBSTITUTION_TEMPLATE;
+ else if (mode == TemplateContinuation)
+ return T_TEMPLATE_TAIL;
+ else
+ return T_STRING_LITERAL;
}
- buf.append('\0');
- _tokenValue = strtod(buf.constData(), nullptr);
- return T_NUMERIC_LITERAL;
+ scanChar();
}
- } else if (_char.isDigit() && !qmlMode()) {
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'");
- return T_ERROR;
}
- QVarLengthArray<char,32> chars;
- chars.append(ch.unicode());
+ _validTokenText = true;
+ _tokenText.resize(0);
+ startCode--;
+ while (startCode != _codePtr - 1)
+ _tokenText += *startCode++;
+
+ while (_codePtr <= _endPtr) {
+ if (unsigned sequenceLength = isLineTerminatorSequence()) {
+ multilineStringLiteral = true;
+ _tokenText += _char;
+ if (sequenceLength == 2)
+ _tokenText += *_codePtr;
+ scanChar();
+ } else if (_char == mode) {
+ scanChar();
- if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) {
- ch = _char; // remember the x or X to use it in the error message below.
+ if (_engine)
+ _tokenSpell = _engine->newStringRef(_tokenText);
- // parse hex integer literal
- chars.append(_char.unicode());
- scanChar(); // consume `x'
+ if (quote == QLatin1Char('`'))
+ _bracesCount = _outerTemplateBraceCount.pop();
- while (isHexDigit(_char)) {
- chars.append(_char.unicode());
+ if (mode == TemplateContinuation)
+ return T_TEMPLATE_TAIL;
+ else if (mode == TemplateHead)
+ return T_NO_SUBSTITUTION_TEMPLATE;
+
+ return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
+ } else if (quote == QLatin1Char('`') && _char == QLatin1Char('$') && *_codePtr == '{') {
+ scanChar();
+ scanChar();
+ _bracesCount = 1;
+ if (_engine)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+
+ return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE);
+ } else if (_char == QLatin1Char('\\')) {
+ scanChar();
+ if (_codePtr > _endPtr) {
+ _errorCode = IllegalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
+ return T_ERROR;
+ }
+
+ QChar u;
+
+ switch (_char.unicode()) {
+ // unicode escape sequence
+ case 'u': {
+ bool ok = false;
+ uint codePoint = decodeUnicodeEscapeCharacter(&ok);
+ if (!ok)
+ return T_ERROR;
+ if (QChar::requiresSurrogates(codePoint)) {
+ // need to use a surrogate pair
+ _tokenText += QChar(QChar::highSurrogate(codePoint));
+ u = QChar::lowSurrogate(codePoint);
+ } else {
+ u = codePoint;
+ }
+ } break;
+
+ // hex escape sequence
+ case 'x': {
+ bool ok = false;
+ u = decodeHexEscapeCharacter(&ok);
+ if (!ok) {
+ _errorCode = IllegalHexadecimalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
+ return T_ERROR;
+ }
+ } break;
+
+ // single character escape sequence
+ case '\\': u = QLatin1Char('\\'); scanChar(); break;
+ case '\'': u = QLatin1Char('\''); scanChar(); break;
+ case '\"': u = QLatin1Char('\"'); scanChar(); break;
+ case 'b': u = QLatin1Char('\b'); scanChar(); break;
+ case 'f': u = QLatin1Char('\f'); scanChar(); break;
+ case 'n': u = QLatin1Char('\n'); scanChar(); break;
+ case 'r': u = QLatin1Char('\r'); scanChar(); break;
+ case 't': u = QLatin1Char('\t'); scanChar(); break;
+ case 'v': u = QLatin1Char('\v'); scanChar(); break;
+
+ case '0':
+ if (! _codePtr->isDigit()) {
+ scanChar();
+ u = QLatin1Char('\0');
+ break;
+ }
+ Q_FALLTHROUGH();
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ _errorCode = IllegalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
+ return T_ERROR;
+
+ case '\r':
+ case '\n':
+ case 0x2028u:
+ case 0x2029u:
+ scanChar();
+ continue;
+
+ default:
+ // non escape character
+ u = _char;
+ scanChar();
+ }
+
+ _tokenText += u;
+ } else {
+ _tokenText += _char;
scanChar();
}
+ }
- if (chars.size() < 3) {
- _errorCode = IllegalHexNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch);
+ _errorCode = UnclosedStringLiteral;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
+ return T_ERROR;
+}
+
+int Lexer::scanNumber(QChar ch)
+{
+ if (ch == QLatin1Char('0')) {
+ if (_char == QLatin1Char('x') || _char == QLatin1Char('X')) {
+ ch = _char; // remember the x or X to use it in the error message below.
+
+ // parse hex integer literal
+ scanChar(); // consume 'x'
+
+ if (!isHexDigit(_char)) {
+ _errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch);
+ return T_ERROR;
+ }
+
+ double d = 0.;
+ while (1) {
+ int digit = ::hexDigit(_char);
+ if (digit < 0)
+ break;
+ d *= 16;
+ d += digit;
+ scanChar();
+ }
+
+ _tokenValue = d;
+ return T_NUMERIC_LITERAL;
+ } else if (_char == QLatin1Char('o') || _char == QLatin1Char('O')) {
+ ch = _char; // remember the o or O to use it in the error message below.
+
+ // parse octal integer literal
+ scanChar(); // consume 'o'
+
+ if (!isOctalDigit(_char.unicode())) {
+ _errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "At least one octal digit is required after '0%1'").arg(ch);
+ return T_ERROR;
+ }
+
+ double d = 0.;
+ while (1) {
+ int digit = ::octalDigit(_char);
+ if (digit < 0)
+ break;
+ d *= 8;
+ d += digit;
+ scanChar();
+ }
+
+ _tokenValue = d;
+ return T_NUMERIC_LITERAL;
+ } else if (_char == QLatin1Char('b') || _char == QLatin1Char('B')) {
+ ch = _char; // remember the b or B to use it in the error message below.
+
+ // parse binary integer literal
+ scanChar(); // consume 'b'
+
+ if (_char.unicode() != '0' && _char.unicode() != '1') {
+ _errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "At least one binary digit is required after '0%1'").arg(ch);
+ return T_ERROR;
+ }
+
+ double d = 0.;
+ while (1) {
+ int digit = 0;
+ if (_char.unicode() == '1')
+ digit = 1;
+ else if (_char.unicode() != '0')
+ break;
+ d *= 2;
+ d += digit;
+ scanChar();
+ }
+
+ _tokenValue = d;
+ return T_NUMERIC_LITERAL;
+ } else if (_char.isDigit() && !qmlMode()) {
+ _errorCode = IllegalCharacter;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'");
return T_ERROR;
}
-
- _tokenValue = integerFromString(chars.constData(), chars.size(), 16);
- return T_NUMERIC_LITERAL;
}
// decimal integer literal
- while (_char.isDigit()) {
- chars.append(_char.unicode());
- scanChar(); // consume the digit
- }
-
- if (_char == QLatin1Char('.')) {
- chars.append(_char.unicode());
- scanChar(); // consume `.'
+ QVarLengthArray<char,32> chars;
+ chars.append(ch.unicode());
+ if (ch != QLatin1Char('.')) {
while (_char.isDigit()) {
chars.append(_char.unicode());
- scanChar();
+ scanChar(); // consume the digit
}
- if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
- if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
- _codePtr[1].isDigit())) {
-
- chars.append(_char.unicode());
- scanChar(); // consume `e'
+ if (_char == QLatin1Char('.')) {
+ chars.append(_char.unicode());
+ scanChar(); // consume `.'
+ }
+ }
- if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
- chars.append(_char.unicode());
- scanChar(); // consume the sign
- }
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
- while (_char.isDigit()) {
- chars.append(_char.unicode());
- scanChar();
- }
- }
- }
- } else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
+ if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
_codePtr[1].isDigit())) {
@@ -1001,12 +1104,6 @@ int Lexer::scanNumber(QChar ch)
}
}
- if (chars.length() == 1) {
- // if we ended up with a single digit, then it was a '0'
- _tokenValue = 0;
- return T_NUMERIC_LITERAL;
- }
-
chars.append('\0');
const char *begin = chars.constData();
@@ -1174,16 +1271,6 @@ bool Lexer::isOctalDigit(ushort c)
return (c >= '0' && c <= '7');
}
-int Lexer::tokenEndLine() const
-{
- return _currentLineNumber;
-}
-
-int Lexer::tokenEndColumn() const
-{
- return _codePtr - _lastLinePtr;
-}
-
QString Lexer::tokenText() const
{
if (_validTokenText)
@@ -1256,6 +1343,7 @@ static const int uriTokens[] = {
QQmlJSGrammar::T_FUNCTION,
QQmlJSGrammar::T_IF,
QQmlJSGrammar::T_IN,
+ QQmlJSGrammar::T_OF,
QQmlJSGrammar::T_INSTANCEOF,
QQmlJSGrammar::T_NEW,
QQmlJSGrammar::T_NULL,
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 11d8081713..a6ac8cb354 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -51,10 +51,11 @@
// We mean it.
//
-#include "qqmljsglobal_p.h"
-#include "qqmljsgrammar_p.h"
+#include <private/qqmljsglobal_p.h>
+#include <private/qqmljsgrammar_p.h>
#include <QtCore/qstring.h>
+#include <QtCore/qstack.h>
QT_QML_BEGIN_NAMESPACE
@@ -62,32 +63,7 @@ namespace QQmlJS {
class Engine;
class DiagnosticMessage;
-
-class QML_PARSER_EXPORT Directives {
-public:
- virtual ~Directives() {}
-
- virtual void pragmaLibrary()
- {
- }
-
- virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
- {
- Q_UNUSED(jsfile);
- Q_UNUSED(module);
- Q_UNUSED(line);
- Q_UNUSED(column);
- }
-
- virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
- {
- Q_UNUSED(uri);
- Q_UNUSED(version);
- Q_UNUSED(module);
- Q_UNUSED(line);
- Q_UNUSED(column);
- }
-};
+class Directives;
class QML_PARSER_EXPORT Lexer: public QQmlJSGrammar
{
@@ -97,10 +73,7 @@ public:
T_BOOLEAN = T_RESERVED_WORD,
T_BYTE = T_RESERVED_WORD,
T_CHAR = T_RESERVED_WORD,
- T_CLASS = T_RESERVED_WORD,
T_DOUBLE = T_RESERVED_WORD,
- T_EXPORT = T_RESERVED_WORD,
- T_EXTENDS = T_RESERVED_WORD,
T_FINAL = T_RESERVED_WORD,
T_FLOAT = T_RESERVED_WORD,
T_GOTO = T_RESERVED_WORD,
@@ -113,8 +86,6 @@ public:
T_PRIVATE = T_RESERVED_WORD,
T_PROTECTED = T_RESERVED_WORD,
T_SHORT = T_RESERVED_WORD,
- T_STATIC = T_RESERVED_WORD,
- T_SUPER = T_RESERVED_WORD,
T_SYNCHRONIZED = T_RESERVED_WORD,
T_THROWS = T_RESERVED_WORD,
T_TRANSIENT = T_RESERVED_WORD,
@@ -124,7 +95,7 @@ public:
enum Error {
NoError,
IllegalCharacter,
- IllegalHexNumber,
+ IllegalNumber,
UnclosedStringLiteral,
IllegalEscapeSequence,
IllegalUnicodeEscapeSequence,
@@ -145,10 +116,29 @@ public:
RegExp_Multiline = 0x04
};
+ enum ParseModeFlags {
+ QmlMode = 0x1,
+ YieldIsKeyword = 0x2,
+ StaticIsKeyword = 0x4
+ };
+
public:
Lexer(Engine *engine);
+ int parseModeFlags() const {
+ int flags = 0;
+ if (qmlMode())
+ flags |= QmlMode|StaticIsKeyword;
+ if (yieldIsKeyWord())
+ flags |= YieldIsKeyword;
+ if (_staticIsKeyword)
+ flags |= StaticIsKeyword;
+ return flags;
+ }
+
bool qmlMode() const;
+ bool yieldIsKeyWord() const { return _generatorLevel != 0; }
+ void setStaticIsKeyword(bool b) { _staticIsKeyword = b; }
QString code() const;
void setCode(const QString &code, int lineno, bool qmlMode = true);
@@ -166,10 +156,7 @@ public:
int tokenLength() const { return _tokenLength; }
int tokenStartLine() const { return _tokenLine; }
- int tokenStartColumn() const { return _tokenStartPtr - _tokenLinePtr + 1; }
-
- int tokenEndLine() const;
- int tokenEndColumn() const;
+ int tokenStartColumn() const { return _tokenColumn; }
inline QStringRef tokenSpell() const { return _tokenSpell; }
double tokenValue() const { return _tokenValue; }
@@ -188,13 +175,23 @@ public:
BalancedParentheses
};
+ void enterGeneratorBody() { ++_generatorLevel; }
+ void leaveGeneratorBody() { --_generatorLevel; }
+
protected:
- int classify(const QChar *s, int n, bool qmlMode);
+ static int classify(const QChar *s, int n, int parseModeFlags);
private:
inline void scanChar();
int scanToken();
int scanNumber(QChar ch);
+ enum ScanStringMode {
+ SingleQuote = '\'',
+ DoubleQuote = '"',
+ TemplateHead = '`',
+ TemplateContinuation = 0
+ };
+ int scanString(ScanStringMode mode);
bool isLineTerminator() const;
unsigned isLineTerminatorSequence() const;
@@ -202,10 +199,9 @@ private:
static bool isDecimalDigit(ushort c);
static bool isHexDigit(QChar c);
static bool isOctalDigit(ushort c);
- static bool isUnicodeEscapeSequence(const QChar *chars);
void syncProhibitAutomaticSemicolon();
- QChar decodeUnicodeEscapeCharacter(bool *ok);
+ uint decodeUnicodeEscapeCharacter(bool *ok);
QChar decodeHexEscapeCharacter(bool *ok);
private:
@@ -218,26 +214,30 @@ private:
const QChar *_codePtr;
const QChar *_endPtr;
- const QChar *_lastLinePtr;
- const QChar *_tokenLinePtr;
const QChar *_tokenStartPtr;
QChar _char;
Error _errorCode;
int _currentLineNumber;
+ int _currentColumnNumber;
double _tokenValue;
// parentheses state
ParenthesesState _parenthesesState;
int _parenthesesCount;
+ // template string stack
+ QStack<int> _outerTemplateBraceCount;
+ int _bracesCount = -1;
+
int _stackToken;
int _patternFlags;
int _tokenKind;
int _tokenLength;
int _tokenLine;
+ int _tokenColumn;
bool _validTokenText;
bool _prohibitAutomaticSemicolon;
@@ -246,6 +246,8 @@ private:
bool _followsClosingBrace;
bool _delimited;
bool _qmlMode;
+ int _generatorLevel = 0;
+ bool _staticIsKeyword = false;
};
} // end of namespace QQmlJS
diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h
index ef443d96bc..9a480f1224 100644
--- a/src/qml/parser/qqmljsmemorypool_p.h
+++ b/src/qml/parser/qqmljsmemorypool_p.h
@@ -83,6 +83,7 @@ public:
free(_blocks);
}
+ qDeleteAll(strings);
}
inline void *allocate(size_t size)
@@ -104,6 +105,11 @@ public:
template <typename Tp> Tp *New() { return new (this->allocate(sizeof(Tp))) Tp(); }
+ QStringRef newString(const QString &string) {
+ strings.append(new QString(string));
+ return QStringRef(strings.last());
+ }
+
private:
Q_NEVER_INLINE void *allocate_helper(size_t size)
{
@@ -143,6 +149,7 @@ private:
int _blockCount = -1;
char *_ptr = nullptr;
char *_end = nullptr;
+ QVector<QString*> strings;
enum
{
@@ -153,12 +160,10 @@ private:
class QML_PARSER_EXPORT Managed
{
- Managed(const Managed &other);
- void operator = (const Managed &other);
-
+ Q_DISABLE_COPY(Managed)
public:
- Managed() {}
- ~Managed() {}
+ Managed() = default;
+ ~Managed() = default;
void *operator new(size_t size, MemoryPool *pool) { return pool->allocate(size); }
void operator delete(void *) {}
diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp
deleted file mode 100644
index 24b04b02f9..0000000000
--- a/src/qml/parser/qqmljsparser.cpp
+++ /dev/null
@@ -1,2010 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qqmljsengine_p.h"
-#include "qqmljslexer_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsmemorypool_p.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qcoreapplication.h>
-
-#include <string.h>
-
-
-
-#include "qqmljsparser_p.h"
-
-#include <QtCore/qvarlengtharray.h>
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is automatically generated from qqmljs.g.
-// Changes should be made to that file, not here. Any change to this file will
-// be lost!
-//
-// To regenerate this file, run:
-// qlalr --no-debug --no-lines --qt qqmljs.g
-//
-
-using namespace QQmlJS;
-
-QT_QML_BEGIN_NAMESPACE
-
-void Parser::reallocateStack()
-{
- if (! stack_size)
- stack_size = 128;
- else
- stack_size <<= 1;
-
- sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
- state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
- location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation)));
- string_stack = reinterpret_cast<QStringRef*> (realloc(static_cast<void *>(string_stack), stack_size * sizeof(QStringRef)));
-}
-
-Parser::Parser(Engine *engine):
- driver(engine),
- pool(engine->pool()),
- tos(0),
- stack_size(0),
- sym_stack(nullptr),
- state_stack(nullptr),
- location_stack(nullptr),
- string_stack(nullptr),
- program(nullptr),
- yylval(0),
- first_token(nullptr),
- last_token(nullptr)
-{
-}
-
-Parser::~Parser()
-{
- if (stack_size) {
- free(sym_stack);
- free(state_stack);
- free(location_stack);
- free(string_stack);
- }
-}
-
-static inline AST::SourceLocation location(Lexer *lexer)
-{
- AST::SourceLocation loc;
- loc.offset = lexer->tokenOffset();
- loc.length = lexer->tokenLength();
- loc.startLine = lexer->tokenStartLine();
- loc.startColumn = lexer->tokenStartColumn();
- return loc;
-}
-
-AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
-{
- QVarLengthArray<QStringRef, 4> nameIds;
- QVarLengthArray<AST::SourceLocation, 4> locations;
-
- AST::ExpressionNode *it = expr;
- while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) {
- nameIds.append(m->name);
- locations.append(m->identifierToken);
- it = m->base;
- }
-
- if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) {
- AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name);
- q->identifierToken = idExpr->identifierToken;
-
- AST::UiQualifiedId *currentId = q;
- for (int i = nameIds.size() - 1; i != -1; --i) {
- currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]);
- currentId->identifierToken = locations[i];
- }
-
- return currentId->finish();
- }
-
- return nullptr;
-}
-
-AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode *expr)
-{
- if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
- AST::UiQualifiedPragmaId *q = new (pool) AST::UiQualifiedPragmaId(idExpr->name);
- q->identifierToken = idExpr->identifierToken;
-
- return q->finish();
- }
-
- return nullptr;
-}
-
-
-bool Parser::parse(int startToken)
-{
- Lexer *lexer = driver->lexer();
- bool hadErrors = false;
- int yytoken = -1;
- int action = 0;
-
- token_buffer[0].token = startToken;
- first_token = &token_buffer[0];
- if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) {
- Directives ignoreDirectives;
- Directives *directives = driver->directives();
- if (!directives)
- directives = &ignoreDirectives;
- DiagnosticMessage error;
- if (!lexer->scanDirectives(directives, &error)) {
- diagnostic_messages.append(error);
- return false;
- }
- token_buffer[1].token = lexer->tokenKind();
- token_buffer[1].dval = lexer->tokenValue();
- token_buffer[1].loc = location(lexer);
- token_buffer[1].spell = lexer->tokenSpell();
- last_token = &token_buffer[2];
- } else {
- last_token = &token_buffer[1];
- }
-
- tos = -1;
- program = nullptr;
-
- do {
- if (++tos == stack_size)
- reallocateStack();
-
- state_stack[tos] = action;
-
- _Lcheck_token:
- if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) {
- yyprevlloc = yylloc;
-
- if (first_token == last_token) {
- yytoken = lexer->lex();
- yylval = lexer->tokenValue();
- yytokenspell = lexer->tokenSpell();
- yylloc = location(lexer);
- } else {
- yytoken = first_token->token;
- yylval = first_token->dval;
- yytokenspell = first_token->spell;
- yylloc = first_token->loc;
- ++first_token;
- }
- }
-
- action = t_action(action, yytoken);
- if (action > 0) {
- if (action != ACCEPT_STATE) {
- yytoken = -1;
- sym(1).dval = yylval;
- stringRef(1) = yytokenspell;
- loc(1) = yylloc;
- } else {
- --tos;
- return ! hadErrors;
- }
- } else if (action < 0) {
- const int r = -action - 1;
- tos -= rhs[r];
-
- switch (r) {
-
-case 0: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 1: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 2: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 3: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 4: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 5: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 6: {
- sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList,
- sym(2).UiObjectMemberList->finish());
-} break;
-
-case 8: {
- sym(1).Node = sym(1).UiHeaderItemList->finish();
-} break;
-
-case 9: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma);
-} break;
-
-case 10: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport);
-} break;
-
-case 11: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma);
-} break;
-
-case 12: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport);
-} break;
-
-case 16: {
- sym(1).UiPragma->semicolonToken = loc(2);
-} break;
-
-case 18: {
- sym(1).UiImport->semicolonToken = loc(2);
-} break;
-
-case 20: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->semicolonToken = loc(3);
-} break;
-
-case 22: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->asToken = loc(3);
- sym(1).UiImport->importIdToken = loc(4);
- sym(1).UiImport->importId = stringRef(4);
- sym(1).UiImport->semicolonToken = loc(5);
-} break;
-
-case 24: {
- sym(1).UiImport->asToken = loc(2);
- sym(1).UiImport->importIdToken = loc(3);
- sym(1).UiImport->importId = stringRef(3);
- sym(1).UiImport->semicolonToken = loc(4);
-} break;
-
-case 25: {
- AST::UiPragma *node = nullptr;
-
- if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) {
- node = new (pool) AST::UiPragma(qualifiedId);
- }
-
- sym(1).Node = node;
-
- if (node) {
- node->pragmaToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
-
- return false; // ### remove me
- }
-} break;
-
-case 26: {
- AST::UiImport *node = nullptr;
-
- if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
- node = new (pool) AST::UiImport(importIdLiteral->value);
- node->fileNameToken = loc(2);
- } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
- node = new (pool) AST::UiImport(qualifiedId);
- node->fileNameToken = loc(2);
- }
-
- sym(1).Node = node;
-
- if (node) {
- node->importToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id or a string literal")));
-
- return false; // ### remove me
- }
-} break;
-
-case 27: {
- sym(1).Node = nullptr;
-} break;
-
-case 28: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
-
-case 29: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
-
-case 30: {
- AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(
- sym(1).UiObjectMemberList, sym(2).UiObjectMember);
- sym(1).Node = node;
-} break;
-
-case 31: {
- sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
-} break;
-
-case 32: {
- AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(
- sym(1).UiArrayMemberList, sym(3).UiObjectMember);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 33: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)nullptr);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 34: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 35: {
- AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId,
- sym(2).UiObjectInitializer);
- sym(1).Node = node;
-} break;
-
-case 37: {
- AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(
- sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish());
- node->colonToken = loc(2);
- node->lbracketToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 38: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 39: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
- node->colonToken = loc(2);
- node->hasOnToken = true;
- sym(1).Node = node;
-} break;
-
-case 47:
-{
- AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(
- sym(1).UiQualifiedId, sym(3).Statement);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 48: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 49: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 50: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 51: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3));
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 52: {
- sym(1).Node = nullptr;
-} break;
-
-case 53: {
- sym(1).Node = sym(1).UiParameterList->finish ();
-} break;
-
-case 54: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2));
- node->propertyTypeToken = loc(1);
- node->identifierToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 55: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4));
- node->propertyTypeToken = loc(3);
- node->commaToken = loc(2);
- node->identifierToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 57: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->parameters = sym(4).UiParameterList;
- node->semicolonToken = loc(6);
- sym(1).Node = node;
-} break;
-
-case 59: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 61: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 63: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 65: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 67: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->typeModifier = stringRef(3);
- node->propertyToken = loc(2);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(7);
- node->semicolonToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 68: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3),
- sym(5).Statement);
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 69: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 70: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 71: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
- propertyName->identifierToken = loc(6);
- propertyName->next = nullptr;
-
- AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(
- propertyName, sym(9).UiArrayMemberList->finish());
- binding->colonToken = loc(7);
- binding->lbracketToken = loc(8);
- binding->rbracketToken = loc(10);
-
- node->binding = binding;
-
- sym(1).Node = node;
-} break;
-
-case 72: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
- propertyName->identifierToken = loc(3);
- propertyName->next = nullptr;
-
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer);
- binding->colonToken = loc(4);
-
- node->binding = binding;
-
- sym(1).Node = node;
-} break;
-
-case 73: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4));
- propertyName->identifierToken = loc(4);
- propertyName->next = nullptr;
-
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer);
- binding->colonToken = loc(5);
-
- node->binding = binding;
-
- sym(1).Node = node;
-} break;
-
-case 74: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
-
-case 75: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
-
-case 76: {
- AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish());
- enumDeclaration->enumToken = loc(1);
- enumDeclaration->rbraceToken = loc(5);
- sym(1).Node = enumDeclaration;
- break;
-}
-
-case 77: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1));
- node->memberToken = loc(1);
- sym(1).Node = node;
- break;
-}
-
-case 78: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval);
- node->memberToken = loc(1);
- node->valueToken = loc(3);
- sym(1).Node = node;
- break;
-}
-
-case 79: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3));
- node->memberToken = loc(3);
- sym(1).Node = node;
- break;
-}
-
-case 80: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval);
- node->memberToken = loc(3);
- node->valueToken = loc(5);
- sym(1).Node = node;
- break;
-}
-
-case 88: {
- AST::ThisExpression *node = new (pool) AST::ThisExpression();
- node->thisToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 89: {
- AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 90: {
- AST::NullExpression *node = new (pool) AST::NullExpression();
- node->nullToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 91: {
- AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
- node->trueToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 92: {
- AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
- node->falseToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 93: {
- AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-case 94:
-case 95: {
- AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 96: {
- bool rx = lexer->scanRegExp(Lexer::NoPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false; // ### remove me
- }
-
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
-
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 97: {
- bool rx = lexer->scanRegExp(Lexer::EqualPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false;
- }
-
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
-
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 98: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) nullptr);
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 99: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 100: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 101: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- (AST::Elision *) nullptr);
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 102: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- sym(4).Elision->finish());
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 103: {
- AST::ObjectLiteral *node = nullptr;
- if (sym(2).Node)
- node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- else
- node = new (pool) AST::ObjectLiteral();
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 104: {
- AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 105: {
- AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
- node->lparenToken = loc(1);
- node->rparenToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 106: {
- if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
- QLatin1String("Ignored annotation")));
-
- sym(1).Expression = mem->base;
- }
-
- if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
- sym(1).UiQualifiedId = qualifiedId;
- } else {
- sym(1).UiQualifiedId = nullptr;
-
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
-
- return false; // ### recover
- }
-} break;
-
-case 107: {
- sym(1).Node = new (pool) AST::ElementList((AST::Elision *) nullptr, sym(1).Expression);
-} break;
-
-case 108: {
- sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression);
-} break;
-
-case 109: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList,
- (AST::Elision *) nullptr, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 110: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(),
- sym(4).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 111: {
- AST::Elision *node = new (pool) AST::Elision();
- node->commaToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 112: {
- AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 113: {
- AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue(
- sym(1).PropertyName, sym(3).Expression);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 114: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(6).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 115: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 116: {
- sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment);
-} break;
-
-case 117: {
- AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList(
- sym(1).PropertyAssignmentList, sym(3).PropertyAssignment);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 118: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 119: {
- AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 120: {
- AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 121: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 159: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 160: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 161: {
- AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
- node->newToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 163: {
- AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
- node->newToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 164: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 165: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 166: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 167: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 168: {
- sym(1).Node = nullptr;
-} break;
-
-case 169: {
- sym(1).Node = sym(1).ArgumentList->finish();
-} break;
-
-case 170: {
- sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
-} break;
-
-case 171: {
- AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 175: {
- AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
- node->incrementToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 176: {
- AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
- node->decrementToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 178: {
- AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
- node->deleteToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 179: {
- AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
- node->voidToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 180: {
- AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
- node->typeofToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 181: {
- AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
- node->incrementToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 182: {
- AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
- node->decrementToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 183: {
- AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
- node->plusToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 184: {
- AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
- node->minusToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 185: {
- AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
- node->tildeToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 186: {
- AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
- node->notToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 188: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mul, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 189: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Div, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 190: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mod, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 192: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Add, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 193: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Sub, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 195: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::LShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 196: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::RShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 197: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::URShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 199: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 200: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 201: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 202: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 203: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 204: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::In, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 206: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 207: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 208: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 209: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 210: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 212: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 213: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 214: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 215: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 217: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 218: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 219: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 220: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 222: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 224: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 226: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 228: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 230: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 232: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 234: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 236: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 238: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 240: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 242: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 244: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 246: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 248: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 249: {
- sym(1).ival = QSOperator::Assign;
-} break;
-
-case 250: {
- sym(1).ival = QSOperator::InplaceMul;
-} break;
-
-case 251: {
- sym(1).ival = QSOperator::InplaceDiv;
-} break;
-
-case 252: {
- sym(1).ival = QSOperator::InplaceMod;
-} break;
-
-case 253: {
- sym(1).ival = QSOperator::InplaceAdd;
-} break;
-
-case 254: {
- sym(1).ival = QSOperator::InplaceSub;
-} break;
-
-case 255: {
- sym(1).ival = QSOperator::InplaceLeftShift;
-} break;
-
-case 256: {
- sym(1).ival = QSOperator::InplaceRightShift;
-} break;
-
-case 257: {
- sym(1).ival = QSOperator::InplaceURightShift;
-} break;
-
-case 258: {
- sym(1).ival = QSOperator::InplaceAnd;
-} break;
-
-case 259: {
- sym(1).ival = QSOperator::InplaceXor;
-} break;
-
-case 260: {
- sym(1).ival = QSOperator::InplaceOr;
-} break;
-
-case 262: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 263: {
- sym(1).Node = nullptr;
-} break;
-
-case 266: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 267: {
- sym(1).Node = nullptr;
-} break;
-
-case 284: {
- AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 285: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).Statement);
-} break;
-
-case 286: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement);
-} break;
-
-case 287: {
- sym(1).Node = nullptr;
-} break;
-
-case 288: {
- sym(1).Node = sym(1).StatementList->finish ();
-} break;
-
-case 290: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- if (sym(1).ival == T_LET)
- s = AST::VariableDeclaration::BlockScope;
- else if (sym(1).ival == T_CONST)
- s = AST::VariableDeclaration::ReadOnlyBlockScope;
-
- AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s));
- node->declarationKindToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 291: {
- sym(1).ival = T_LET;
-} break;
-
-case 292: {
- sym(1).ival = T_CONST;
-} break;
-
-case 293: {
- sym(1).ival = T_VAR;
-} break;
-
-case 294: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
-
-case 295: {
- AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(
- sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 296: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
-
-case 297: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
-} break;
-
-case 298: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 299: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 300: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
-
-case 301: {
- sym(1).Node = nullptr;
-} break;
-
-case 303: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
-
-case 304: {
- sym(1).Node = nullptr;
-} break;
-
-case 306: {
- AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
- node->semicolonToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 308: {
- AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 309: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->elseToken = loc(6);
- sym(1).Node = node;
-} break;
-
-case 310: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 313: {
- AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
- node->doToken = loc(1);
- node->whileToken = loc(3);
- node->lparenToken = loc(4);
- node->rparenToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 314: {
- AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
- node->whileToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 315: {
- AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Expression, sym(9).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->firstSemicolonToken = loc(4);
- node->secondSemicolonToken = loc(6);
- node->rparenToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 316: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::LocalForStatement *node = new (pool) AST::LocalForStatement(
- sym(4).VariableDeclarationList->finish(s), sym(6).Expression,
- sym(8).Expression, sym(10).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->firstSemicolonToken = loc(5);
- node->secondSemicolonToken = loc(7);
- node->rparenToken = loc(9);
- sym(1).Node = node;
-} break;
-
-case 317: {
- AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->inToken = loc(4);
- node->rparenToken = loc(6);
- sym(1).Node = node;
-} break;
-
-case 318: {
- AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement(
- sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->inToken = loc(5);
- node->rparenToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 320: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
- node->continueToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 322: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
- node->continueToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 324: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
- node->breakToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 326: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
- node->breakToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 328: {
- AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
- node->returnToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 329: {
- AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
- node->withToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 330: {
- AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
- node->switchToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 331: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 332: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 333: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
-} break;
-
-case 334: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
-} break;
-
-case 335: {
- sym(1).Node = nullptr;
-} break;
-
-case 336: {
- sym(1).Node = sym(1).CaseClauses->finish ();
-} break;
-
-case 337: {
- AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
- node->caseToken = loc(1);
- node->colonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 338: {
- AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
- node->defaultToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 339: {
- AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
- node->identifierToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 341: {
- AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
- node->throwToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 342: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 343: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 344: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 345: {
- AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block);
- node->catchToken = loc(1);
- node->lparenToken = loc(2);
- node->identifierToken = loc(3);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 346: {
- AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
- node->finallyToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 348: {
- AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
- node->debuggerToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 350: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 351: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- if (! stringRef(2).isNull())
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 352: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody);
- node->functionToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 353: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 354: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3));
- node->commaToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 355: {
- sym(1).Node = nullptr;
-} break;
-
-case 356: {
- sym(1).Node = sym(1).FormalParameterList->finish ();
-} break;
-
-case 357: {
- sym(1).Node = nullptr;
-} break;
-
-case 359: {
- sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ());
-} break;
-
-case 361: {
- sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ());
-} break;
-
-case 362: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement);
-} break;
-
-case 363: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement);
-} break;
-
-case 364: {
- sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement);
-} break;
-
-case 365: {
- sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration);
-} break;
-
-case 366: {
- sym(1).Node = nullptr;
-} break;
-
- } // switch
- action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT);
- } // if
- } while (action != 0);
-
- if (first_token == last_token) {
- const int errorState = state_stack[tos];
-
- // automatic insertion of `;'
- if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken))
- || t_action(errorState, T_COMPATIBILITY_SEMICOLON))) {
- SavedToken &tk = token_buffer[0];
- tk.token = yytoken;
- tk.dval = yylval;
- tk.spell = yytokenspell;
- tk.loc = yylloc;
-
- yylloc = yyprevlloc;
- yylloc.offset += yylloc.length;
- yylloc.startColumn += yylloc.length;
- yylloc.length = 0;
-
- //const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'");
- //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg));
-
- first_token = &token_buffer[0];
- last_token = &token_buffer[1];
-
- yytoken = T_SEMICOLON;
- yylval = 0;
-
- action = errorState;
-
- goto _Lcheck_token;
- }
-
- hadErrors = true;
-
- token_buffer[0].token = yytoken;
- token_buffer[0].dval = yylval;
- token_buffer[0].spell = yytokenspell;
- token_buffer[0].loc = yylloc;
-
- token_buffer[1].token = yytoken = lexer->lex();
- token_buffer[1].dval = yylval = lexer->tokenValue();
- token_buffer[1].spell = yytokenspell = lexer->tokenSpell();
- token_buffer[1].loc = yylloc = location(lexer);
-
- if (t_action(errorState, yytoken)) {
- QString msg;
- int token = token_buffer[0].token;
- if (token < 0 || token >= TERMINAL_COUNT)
- msg = QCoreApplication::translate("QQmlParser", "Syntax error");
- else
- msg = QCoreApplication::translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
-
- action = errorState;
- goto _Lcheck_token;
- }
-
- static int tokens[] = {
- T_PLUS,
- T_EQ,
-
- T_COMMA,
- T_COLON,
- T_SEMICOLON,
-
- T_RPAREN, T_RBRACKET, T_RBRACE,
-
- T_NUMERIC_LITERAL,
- T_IDENTIFIER,
-
- T_LPAREN, T_LBRACKET, T_LBRACE,
-
- EOF_SYMBOL
- };
-
- for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) {
- int a = t_action(errorState, *tk);
- if (a > 0 && t_action(a, yytoken)) {
- const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
-
- yytoken = *tk;
- yylval = 0;
- yylloc = token_buffer[0].loc;
- yylloc.length = 0;
-
- first_token = &token_buffer[0];
- last_token = &token_buffer[2];
-
- action = errorState;
- goto _Lcheck_token;
- }
- }
-
- for (int tk = 1; tk < TERMINAL_COUNT; ++tk) {
- if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM ||
- tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION ||
- tk == T_FEED_JS_SOURCE_ELEMENT)
- continue;
-
- int a = t_action(errorState, tk);
- if (a > 0 && t_action(a, yytoken)) {
- const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
-
- yytoken = tk;
- yylval = 0;
- yylloc = token_buffer[0].loc;
- yylloc.length = 0;
-
- action = errorState;
- goto _Lcheck_token;
- }
- }
-
- const QString msg = QCoreApplication::translate("QQmlParser", "Syntax error");
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
- }
-
- return false;
-}
-
-QT_QML_END_NAMESPACE
-
-
diff --git a/src/qml/parser/qqmljsparser_p.h b/src/qml/parser/qqmljsparser_p.h
deleted file mode 100644
index b4aecd2f08..0000000000
--- a/src/qml/parser/qqmljsparser_p.h
+++ /dev/null
@@ -1,258 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is automatically generated from qqmljs.g.
-// Changes should be made to that file, not here. Any change to this file will
-// be lost!
-//
-// To regenerate this file, run:
-// qlalr --no-debug --no-lines --qt qqmljs.g
-//
-
-#ifndef QQMLJSPARSER_P_H
-#define QQMLJSPARSER_P_H
-
-#include "qqmljsglobal_p.h"
-#include "qqmljsgrammar_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsengine_p.h"
-
-#include <QtCore/qlist.h>
-#include <QtCore/qstring.h>
-
-QT_QML_BEGIN_NAMESPACE
-
-namespace QQmlJS {
-
-class Engine;
-
-class QML_PARSER_EXPORT Parser: protected QQmlJSGrammar
-{
-public:
- union Value {
- int ival;
- double dval;
- AST::ArgumentList *ArgumentList;
- AST::CaseBlock *CaseBlock;
- AST::CaseClause *CaseClause;
- AST::CaseClauses *CaseClauses;
- AST::Catch *Catch;
- AST::DefaultClause *DefaultClause;
- AST::ElementList *ElementList;
- AST::Elision *Elision;
- AST::ExpressionNode *Expression;
- AST::Finally *Finally;
- AST::FormalParameterList *FormalParameterList;
- AST::FunctionBody *FunctionBody;
- AST::FunctionDeclaration *FunctionDeclaration;
- AST::Node *Node;
- AST::PropertyName *PropertyName;
- AST::PropertyAssignment *PropertyAssignment;
- AST::PropertyAssignmentList *PropertyAssignmentList;
- AST::SourceElement *SourceElement;
- AST::SourceElements *SourceElements;
- AST::Statement *Statement;
- AST::StatementList *StatementList;
- AST::Block *Block;
- AST::VariableDeclaration *VariableDeclaration;
- AST::VariableDeclarationList *VariableDeclarationList;
-
- AST::UiProgram *UiProgram;
- AST::UiHeaderItemList *UiHeaderItemList;
- AST::UiPragma *UiPragma;
- AST::UiImport *UiImport;
- AST::UiParameterList *UiParameterList;
- AST::UiPublicMember *UiPublicMember;
- AST::UiObjectDefinition *UiObjectDefinition;
- AST::UiObjectInitializer *UiObjectInitializer;
- AST::UiObjectBinding *UiObjectBinding;
- AST::UiScriptBinding *UiScriptBinding;
- AST::UiArrayBinding *UiArrayBinding;
- AST::UiObjectMember *UiObjectMember;
- AST::UiObjectMemberList *UiObjectMemberList;
- AST::UiArrayMemberList *UiArrayMemberList;
- AST::UiQualifiedId *UiQualifiedId;
- AST::UiQualifiedPragmaId *UiQualifiedPragmaId;
- AST::UiEnumMemberList *UiEnumMemberList;
- };
-
-public:
- Parser(Engine *engine);
- ~Parser();
-
- // parse a UI program
- bool parse() { return parse(T_FEED_UI_PROGRAM); }
- bool parseStatement() { return parse(T_FEED_JS_STATEMENT); }
- bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); }
- bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); }
- bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); }
- bool parseProgram() { return parse(T_FEED_JS_PROGRAM); }
-
- AST::UiProgram *ast() const
- { return AST::cast<AST::UiProgram *>(program); }
-
- AST::Statement *statement() const
- {
- if (! program)
- return nullptr;
-
- return program->statementCast();
- }
-
- AST::ExpressionNode *expression() const
- {
- if (! program)
- return nullptr;
-
- return program->expressionCast();
- }
-
- AST::UiObjectMember *uiObjectMember() const
- {
- if (! program)
- return nullptr;
-
- return program->uiObjectMemberCast();
- }
-
- AST::Node *rootNode() const
- { return program; }
-
- QList<DiagnosticMessage> diagnosticMessages() const
- { return diagnostic_messages; }
-
- inline DiagnosticMessage diagnosticMessage() const
- {
- for (const DiagnosticMessage &d : diagnostic_messages) {
- if (d.kind != DiagnosticMessage::Warning)
- return d;
- }
-
- return DiagnosticMessage();
- }
-
- inline QString errorMessage() const
- { return diagnosticMessage().message; }
-
- inline int errorLineNumber() const
- { return diagnosticMessage().loc.startLine; }
-
- inline int errorColumnNumber() const
- { return diagnosticMessage().loc.startColumn; }
-
-protected:
- bool parse(int startToken);
-
- void reallocateStack();
-
- inline Value &sym(int index)
- { return sym_stack [tos + index - 1]; }
-
- inline QStringRef &stringRef(int index)
- { return string_stack [tos + index - 1]; }
-
- inline AST::SourceLocation &loc(int index)
- { return location_stack [tos + index - 1]; }
-
- AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
- AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr);
-
-protected:
- Engine *driver;
- MemoryPool *pool;
- int tos;
- int stack_size;
- Value *sym_stack;
- int *state_stack;
- AST::SourceLocation *location_stack;
- QStringRef *string_stack;
-
- AST::Node *program;
-
- // error recovery
- enum { TOKEN_BUFFER_SIZE = 3 };
-
- struct SavedToken {
- int token;
- double dval;
- AST::SourceLocation loc;
- QStringRef spell;
- };
-
- double yylval;
- QStringRef yytokenspell;
- AST::SourceLocation yylloc;
- AST::SourceLocation yyprevlloc;
-
- SavedToken token_buffer[TOKEN_BUFFER_SIZE];
- SavedToken *first_token;
- SavedToken *last_token;
-
- QList<DiagnosticMessage> diagnostic_messages;
-};
-
-} // end of namespace QQmlJS
-
-
-
-#define J_SCRIPT_REGEXPLITERAL_RULE1 96
-
-#define J_SCRIPT_REGEXPLITERAL_RULE2 97
-
-QT_QML_END_NAMESPACE
-
-
-
-#endif // QQMLJSPARSER_P_H
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index 2137877427..a76a87b153 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -31,7 +31,7 @@ DEFINES += QT_NO_FOREACH
!equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") {
QML_COMPILE_HASH = $$tag
} else:exists($$PWD/../../.git) {
- commit = $$system(git describe --tags --always --long --dirty)
+ commit = $$system(git rev-parse HEAD)
QML_COMPILE_HASH = $$commit
}
compile_hash_contents = \
@@ -73,7 +73,7 @@ include(jsruntime/jsruntime.pri)
include(jit/jit.pri)
include(qml/qml.pri)
include(debugger/debugger.pri)
-qtConfig(animation) {
+qtConfig(qml-animation) {
include(animations/animations.pri)
}
include(types/types.pri)
diff --git a/src/qml/qml/ftw/qflagpointer_p.h b/src/qml/qml/ftw/qflagpointer_p.h
index 91ce74bec9..71b41cd30b 100644
--- a/src/qml/qml/ftw/qflagpointer_p.h
+++ b/src/qml/qml/ftw/qflagpointer_p.h
@@ -82,6 +82,8 @@ public:
inline T *data() const;
+ inline explicit operator bool() const;
+
private:
quintptr ptr_value = 0;
@@ -230,6 +232,12 @@ T *QFlagPointer<T>::data() const
return (T *)(ptr_value & ~FlagsMask);
}
+template<typename T>
+QFlagPointer<T>::operator bool() const
+{
+ return data() != nullptr;
+}
+
template<typename T, typename T2>
QBiPointer<T, T2>::QBiPointer()
{
diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h
index 3cfb345b30..d32a08e0f5 100644
--- a/src/qml/qml/ftw/qqmlrefcount_p.h
+++ b/src/qml/qml/ftw/qqmlrefcount_p.h
@@ -85,19 +85,23 @@ public:
inline QQmlRefPointer();
inline QQmlRefPointer(T *, Mode m = AddRef);
inline QQmlRefPointer(const QQmlRefPointer<T> &);
+ inline QQmlRefPointer(QQmlRefPointer<T> &&);
inline ~QQmlRefPointer();
inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o);
+ inline QQmlRefPointer<T> &operator=(QQmlRefPointer<T> &&o);
inline bool isNull() const { return !o; }
inline T* operator->() const { return o; }
inline T& operator*() const { return *o; }
- inline operator T*() const { return o; }
+ explicit inline operator bool() const { return o != nullptr; }
inline T* data() const { return o; }
inline QQmlRefPointer<T> &adopt(T *);
+ inline T* take() { T *res = o; o = nullptr; return res; }
+
private:
T *o;
};
@@ -156,6 +160,12 @@ QQmlRefPointer<T>::QQmlRefPointer(const QQmlRefPointer<T> &other)
if (o) o->addref();
}
+template <class T>
+QQmlRefPointer<T>::QQmlRefPointer(QQmlRefPointer<T> &&other)
+ : o(other.take())
+{
+}
+
template<class T>
QQmlRefPointer<T>::~QQmlRefPointer()
{
@@ -171,6 +181,14 @@ QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other)
return *this;
}
+template <class T>
+QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(QQmlRefPointer<T> &&other)
+{
+ QQmlRefPointer<T> m(std::move(other));
+ qSwap(o, m.o);
+ return *this;
+}
+
/*!
Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership
of the callers reference of other.
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index 412dc6cba2..6d69294c17 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -20,7 +20,6 @@ SOURCES += \
$$PWD/qqmlinfo.cpp \
$$PWD/qqmlerror.cpp \
$$PWD/qqmlvaluetype.cpp \
- $$PWD/qqmlxmlhttprequest.cpp \
$$PWD/qqmlcleanup.cpp \
$$PWD/qqmlpropertycache.cpp \
$$PWD/qqmlnotifier.cpp \
@@ -31,7 +30,6 @@ SOURCES += \
$$PWD/qqmlextensionplugin.cpp \
$$PWD/qqmlimport.cpp \
$$PWD/qqmllist.cpp \
- $$PWD/qqmllocale.cpp \
$$PWD/qqmljavascriptexpression.cpp \
$$PWD/qqmlabstractbinding.cpp \
$$PWD/qqmlvaluetypeproxybinding.cpp \
@@ -85,7 +83,6 @@ HEADERS += \
$$PWD/qqmldata_p.h \
$$PWD/qqmlerror.h \
$$PWD/qqmlvaluetype_p.h \
- $$PWD/qqmlxmlhttprequest_p.h \
$$PWD/qqmlcleanup_p.h \
$$PWD/qqmlpropertycache_p.h \
$$PWD/qqmlpropertyindex_p.h \
@@ -99,7 +96,6 @@ HEADERS += \
$$PWD/qqmlimport_p.h \
$$PWD/qqmlextensionplugin.h \
$$PWD/qqmlscriptstring_p.h \
- $$PWD/qqmllocale_p.h \
$$PWD/qqmlcomponentattached_p.h \
$$PWD/qqmljavascriptexpression_p.h \
$$PWD/qqmlabstractbinding_p.h \
@@ -120,5 +116,22 @@ HEADERS += \
$$PWD/qqmldelayedcallqueue_p.h \
$$PWD/qqmlloggingcategory_p.h
+qtConfig(qml-xml-http-request) {
+ HEADERS += \
+ $$PWD/qqmlxmlhttprequest_p.h
+
+ SOURCES += \
+ $$PWD/qqmlxmlhttprequest.cpp
+
+}
+
+qtConfig(qml-locale) {
+ HEADERS += \
+ $$PWD/qqmllocale_p.h
+
+ SOURCES += \
+ $$PWD/qqmllocale.cpp
+}
+
include(ftw/ftw.pri)
include(v8/v8.pri)
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 213f23cd98..2a8e236905 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -645,6 +645,8 @@ inline int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, i
return QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, &type);
}
+int Q_QML_EXPORT qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QObject)
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 30a18440a8..a4b3f1f4e4 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -337,7 +337,7 @@ protected:
class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
public:
- QQmlTranslationBinding(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Binding *binding)
+ QQmlTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
setCompilationUnit(compilationUnit);
m_binding = binding;
@@ -374,11 +374,13 @@ public:
}
}
+ bool hasDependencies() const override final { return true; }
+
private:
const QV4::CompiledData::Binding *m_binding;
};
-QQmlBinding *QQmlBinding::createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
+QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
{
QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding);
@@ -663,6 +665,11 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
return dependencies;
}
+bool QQmlBinding::hasDependencies() const
+{
+ return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured();
+}
+
class QObjectPointerBinding: public QQmlNonbindingBinding
{
QQmlMetaObject targetMetaObject;
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index a1295bd0ac..f192de4342 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -79,7 +79,7 @@ public:
const QString &url = QString(), quint16 lineNumber = 0);
static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function,
QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope);
- static QQmlBinding *createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding,
+ static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding,
QObject *obj, QQmlContextData *ctxt);
~QQmlBinding() override;
@@ -118,6 +118,7 @@ public:
* Call this method from the UI thread.
*/
QVector<QQmlProperty> dependencies() const;
+ virtual bool hasDependencies() const;
protected:
virtual void doUpdate(const DeleteWatcher &watcher,
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 8885e69161..9b43ea0531 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -334,7 +334,7 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
emit q->progressChanged(p);
}
-void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
+void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data)
{
url = data->finalUrl();
compilationUnit = data->compilationUnit();
@@ -343,15 +343,12 @@ void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
Q_ASSERT(data->isError());
state.errors = data->errors();
}
-
- data->release();
}
void QQmlComponentPrivate::clear()
{
if (typeData) {
typeData->unregisterCallback(this);
- typeData->release();
typeData = nullptr;
}
@@ -387,7 +384,7 @@ QQmlComponent::~QQmlComponent()
if (d->typeData) {
d->typeData->unregisterCallback(d);
- d->typeData->release();
+ d->typeData = nullptr;
}
}
@@ -580,7 +577,7 @@ void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
d->url = url;
- QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url);
+ QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url);
if (typeData->isCompleteOrError()) {
d->fromTypeData(typeData);
@@ -667,7 +664,7 @@ void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::Compilatio
? QQmlTypeLoader::Asynchronous
: QQmlTypeLoader::PreferSynchronous;
- QQmlTypeData *data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode);
+ QQmlRefPointer<QQmlTypeData> data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode);
if (data->isCompleteOrError()) {
fromTypeData(data);
@@ -848,13 +845,10 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
// Do not create infinite recursion in object creation
static const int maxCreationDepth = 10;
- if (++creationDepth.localData() >= maxCreationDepth) {
+ if (creationDepth.localData() >= maxCreationDepth) {
qWarning("QQmlComponent: Component creation is recursing - aborting");
- --creationDepth.localData();
return nullptr;
}
- Q_ASSERT(creationDepth.localData() >= 1);
- depthIncreased = true;
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
@@ -878,10 +872,6 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
ddata->indestructible = true;
ddata->explicitIndestructibleSet = true;
ddata->rootObjectInCreation = false;
- } else {
- Q_ASSERT(creationDepth.localData() >= 1);
- --creationDepth.localData();
- depthIncreased = false;
}
return rv;
@@ -955,14 +945,10 @@ void QQmlComponent::completeCreate()
void QQmlComponentPrivate::completeCreate()
{
if (state.completePending) {
+ ++creationDepth.localData();
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
complete(ep, &state);
- }
-
- if (depthIncreased) {
- Q_ASSERT(creationDepth.localData() >= 1);
--creationDepth.localData();
- depthIncreased = false;
}
}
@@ -1421,9 +1407,9 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
QQmlComponentExtension *e = componentExtension(args->v4engine());
- QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocObject<QV4::QmlIncubatorObject>(mode));
+ QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode));
QV4::ScopedObject p(scope, e->incubationProto.value());
- r->setPrototype(p);
+ r->setPrototypeOf(p);
if (!valuemap->isUndefined())
r->d()->valuemap.set(scope.engine, valuemap);
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 2a8d36f317..9b2db4bccf 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -79,7 +79,7 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public
public:
QQmlComponentPrivate()
- : typeData(nullptr), progress(0.), start(-1), engine(nullptr), creationContext(nullptr), depthIncreased(false) {}
+ : progress(0.), start(-1), engine(nullptr), creationContext(nullptr) {}
void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);
@@ -95,11 +95,11 @@ public:
QQmlContextData *context,
QQmlContextData *forContext);
- QQmlTypeData *typeData;
+ QQmlRefPointer<QQmlTypeData> typeData;
void typeDataReady(QQmlTypeData *) override;
void typeDataProgress(QQmlTypeData *, qreal) override;
- void fromTypeData(QQmlTypeData *data);
+ void fromTypeData(const QQmlRefPointer<QQmlTypeData> &data);
QUrl url;
qreal progress;
@@ -136,7 +136,6 @@ public:
QQmlEngine *engine;
QQmlGuardedContextData creationContext;
- bool depthIncreased;
void clear();
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 59fefde893..2468de6857 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -232,7 +232,7 @@ public:
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
QVector<DeferredData *> deferredData;
- void deferData(int objectIndex, QV4::CompiledData::CompilationUnit *, QQmlContextData *);
+ void deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QQmlContextData *);
void releaseDeferredData();
QV4::WeakValue jsWrapper;
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 5bcf5cd586..61cb0a9065 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -71,7 +71,7 @@ void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *en
*jsCallData->thisObject = QV4::Encode::undefined();
for (int i = 0; i < argCount; i++) {
- jsCallData->args[i] = array->getIndexed(i);
+ jsCallData->args[i] = array->get(i);
}
callback->call(jsCallData);
@@ -186,7 +186,7 @@ void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4
QV4::ScopedArrayObject array(scope, engine->newArrayObject(length));
uint i = 0;
for (int j = offset, ej = argc; j < ej; ++i, ++j)
- array->putIndexed(i, argv[j]);
+ array->put(i, argv[j]);
dfc.m_args.set(engine, array);
}
diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp
index 4cca8a4d58..8c89cf0e61 100644
--- a/src/qml/qml/qqmldirparser.cpp
+++ b/src/qml/qml/qqmldirparser.cpp
@@ -107,6 +107,7 @@ bool QQmlDirParser::parse(const QString &source)
_components.clear();
_scripts.clear();
_designerSupported = false;
+ _className.clear();
quint16 lineNumber = 0;
bool firstLine = true;
@@ -196,7 +197,8 @@ bool QQmlDirParser::parse(const QString &source)
continue;
}
- // Ignore these. qmlimportscanner uses them.
+ _className = sections[1];
+
} else if (sections[0] == QLatin1String("internal")) {
if (sectionCount != 3) {
reportError(lineNumber, 0,
@@ -377,6 +379,11 @@ bool QQmlDirParser::designerSupported() const
return _designerSupported;
}
+QString QQmlDirParser::className() const
+{
+ return _className;
+}
+
QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component)
{
const QString output = QStringLiteral("{%1 %2.%3}").
diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h
index 820c40238d..d7e29813d1 100644
--- a/src/qml/qml/qqmldirparser_p.h
+++ b/src/qml/qml/qqmldirparser_p.h
@@ -135,6 +135,8 @@ public:
QList<TypeInfo> typeInfos() const;
#endif
+ QString className() const;
+
private:
bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true);
void reportError(quint16 line, quint16 column, const QString &message);
@@ -150,6 +152,7 @@ private:
#ifdef QT_CREATOR
QList<TypeInfo> _typeInfos;
#endif
+ QString _className;
};
typedef QHash<QString,QQmlDirParser::Component> QQmlDirComponents;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index c6b39581a7..119120572c 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -48,7 +48,6 @@
#include "qqmlcomponent.h"
#include "qqmlvme_p.h"
#include "qqmlstringconverters_p.h"
-#include "qqmlxmlhttprequest_p.h"
#include "qqmlscriptstring.h"
#include "qqmlglobal_p.h"
#include "qqmlcomponent_p.h"
@@ -79,13 +78,17 @@
#include <private/qobject_p.h>
#include <private/qmetaobject_p.h>
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <private/qqmlbind_p.h>
#include <private/qqmlconnections_p.h>
-#if QT_CONFIG(animation)
+#if QT_CONFIG(qml_animation)
#include <private/qqmltimer_p.h>
#endif
+#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
+#endif
#include <private/qqmlplatform_p.h>
#include <private/qquickpackage_p.h>
#if QT_CONFIG(qml_delegate_model)
@@ -219,21 +222,25 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int
qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8
qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3
qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections");
-#if QT_CONFIG(animation)
+#if QT_CONFIG(qml_animation)
qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
#endif
qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser);
qmlRegisterType<QQmlInstanceModel>();
- qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "LoggingCategory"); //Only available in >=2.8
+
+ qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8
+ qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 12, "LoggingCategory"); //Only available in >=2.12
}
// These QtQuick types' implementation resides in the QtQml module
void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor)
{
+#if QT_CONFIG(qml_list_model)
qmlRegisterType<QQmlListElement>(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility
qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility
+#endif
qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript");
qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package");
#if QT_CONFIG(qml_delegate_model)
@@ -250,7 +257,9 @@ void QQmlEnginePrivate::defineQtQuick2Module()
// register the QtQuick2 types which are implemented in the QtQml module.
registerQtQuick2Types("QtQuick",2,0);
+#if QT_CONFIG(qml_locale)
qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
+#endif
// Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward
qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR);
@@ -951,7 +960,9 @@ void QQmlEnginePrivate::init()
if (baseModulesUninitialized) {
qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); // required for the Compiler.
registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks.
+#if QT_CONFIG(qml_locale)
qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()"));
+#endif
// Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward
qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR);
@@ -1365,6 +1376,71 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
}
/*!
+ \fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId)
+
+ Returns the instance of a singleton type that was registered under \a qmlTypeId.
+
+ The template argument \e T may be either QJSValue or a pointer to a QObject-derived
+ type and depends on how the singleton was registered. If no instance of \e T has been
+ created yet, it is created now. If \a qmlTypeId does not represent a valid singleton
+ type, either a default constructed QJSValue or a \c nullptr is returned.
+
+ QObject* example:
+ \code
+ class MySingleton : public QObject {
+ Q_OBJECT
+ static int typeId;
+ // ...
+ };
+
+ // Register with QObject* callback
+ MySingleton::typeId = qmlRegisterSingletonType<MySingleton>(...);
+
+ // Retrieve as QObject*
+ QQmlEngine engine;
+ MySingleton* instance = engine.singletonInstance<MySingleton*>(MySingleton::typeId);
+ \endcode
+
+ QJSValue example:
+ \code
+ // Register with QJSValue callback
+ int typeId = qmlRegisterSingletonType(...);
+
+ // Retrieve as QJSValue
+ QQmlEngine engine;
+ QJSValue instance = engine.singletonInstance<QJSValue>(typeId);
+ \endcode
+
+ It is recommended to store the QML type id during registration, e.g. as a static member
+ in the singleton class. Otherwise, a costly lookup via qmlTypeId() has to be performed
+ at run-time.
+
+ \sa qmlRegisterSingletonType(), qmlTypeId()
+ \since 5.12
+*/
+template<>
+QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
+{
+ QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType);
+
+ if (!type.isValid() || !type.isSingleton())
+ return QJSValue();
+
+ QQmlType::SingletonInstanceInfo* info = type.singletonInstanceInfo();
+ info->init(this);
+
+ if (QObject* o = info->qobjectApi(this))
+ return this->newQObject(o);
+ else {
+ QJSValue value = info->scriptApi(this);
+ if (!value.isUndefined())
+ return value;
+ }
+
+ return QJSValue();
+}
+
+/*!
Refreshes all binding expressions that use strings marked for translation.
Call this function after you have installed a new translator with
@@ -1699,7 +1775,7 @@ void QQmlData::NotifyList::layout()
todo = nullptr;
}
-void QQmlData::deferData(int objectIndex, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *context)
+void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *context)
{
QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
deferData->deferredIdx = objectIndex;
@@ -2266,7 +2342,7 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache());
+ return QQmlMetaObject((*iter)->rootPropertyCache().data());
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.baseMetaObject());
@@ -2278,7 +2354,7 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache());
+ return QQmlMetaObject((*iter)->rootPropertyCache().data());
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.metaObject());
@@ -2290,7 +2366,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache();
+ return (*iter)->rootPropertyCache().data();
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2303,7 +2379,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache();
+ return (*iter)->rootPropertyCache().data();
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h
index 73ad2754c8..871e6bd9b4 100644
--- a/src/qml/qml/qqmlengine.h
+++ b/src/qml/qml/qqmlengine.h
@@ -143,6 +143,9 @@ public:
bool outputWarningsToStandardError() const;
void setOutputWarningsToStandardError(bool);
+ template<typename T>
+ T singletonInstance(int qmlTypeId);
+
public Q_SLOTS:
void retranslate();
@@ -167,6 +170,19 @@ private:
Q_DECLARE_PRIVATE(QQmlEngine)
};
+template<>
+Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId);
+
+template<typename T>
+T QQmlEngine::singletonInstance(int qmlTypeId) {
+ QJSValue instance = singletonInstance<QJSValue>(qmlTypeId);
+ if (!instance.isQObject())
+ return nullptr;
+
+ QObject *object = instance.toQObject();
+ return qobject_cast<T>(object);
+}
+
QT_END_NAMESPACE
#endif // QQMLENGINE_H
diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h
index 808bf4c709..3ac63926a0 100644
--- a/src/qml/qml/qqmlguard_p.h
+++ b/src/qml/qml/qqmlguard_p.h
@@ -106,6 +106,34 @@ protected:
virtual void objectDestroyed(T *) {}
};
+template <typename T>
+class QQmlStrongJSQObjectReference : public QQmlGuard<T>
+{
+public:
+ void setObject(T *o, QObject *parent) {
+ T *old = this->object();
+ if (o == old)
+ return;
+
+ if (m_jsOwnership && old && old->parent() == parent)
+ QQml_setParent_noEvent(old, nullptr);
+
+ this->QQmlGuard<T>::operator=(o);
+
+ if (o && !o->parent() && !QQmlData::keepAliveDuringGarbageCollection(o)) {
+ m_jsOwnership = true;
+ QQml_setParent_noEvent(o, parent);
+ } else {
+ m_jsOwnership = false;
+ }
+ }
+
+private:
+ using QQmlGuard<T>::setObject;
+ using QQmlGuard<T>::operator=;
+ bool m_jsOwnership = false;
+};
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QQmlGuard<QObject>)
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 80ebab5ca3..1c37894751 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -737,7 +737,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
int *vmajor, int *vminor, QQmlType *type_return, QString *base,
bool *typeRecursionDetected,
QQmlType::RegistrationType registrationType,
- QQmlImport::RecursionRestriction recursionRestriction) const
+ QQmlImport::RecursionRestriction recursionRestriction,
+ QList<QQmlError> *errors) const
{
if (majversion >= 0 && minversion >= 0) {
QQmlType t = QQmlMetaType::qmlType(type, uri, majversion, minversion);
@@ -820,8 +821,19 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
};
for (uint i = 0; i < sizeof(urlsToTry) / sizeof(urlsToTry[0]); ++i) {
const QString url = urlsToTry[i];
- exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(url)).isEmpty();
+ const QString localPath = QQmlFile::urlToLocalFileOrQrc(url);
+ exists = !typeLoader->absoluteFilePath(localPath).isEmpty();
if (exists) {
+ // don't let function.qml confuse the use of "new Function(...)" for example.
+ if (!QQml_isFileCaseCorrect(localPath)) {
+ exists = false;
+ if (errors) {
+ QQmlError caseError;
+ caseError.setDescription(QLatin1String("File name case mismatch"));
+ errors->append(caseError);
+ }
+ break;
+ }
qmlUrl = url;
break;
}
@@ -908,7 +920,7 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS
for (int i=0; i<imports.count(); ++i) {
const QQmlImportInstance *import = imports.at(i);
if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base,
- &typeRecursionDetected, registrationType, recursionRestriction)) {
+ &typeRecursionDetected, registrationType, recursionRestriction, errors)) {
if (qmlCheckTypes()) {
// check for type clashes
for (int j = i+1; j<imports.count(); ++j) {
@@ -2050,7 +2062,7 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b
if (QQmlExtensionPlugin *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
// basepath should point to the directory of the module, not the plugin file itself:
- QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QUrl::fromLocalFile(basePath);
+ QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath);
}
iface->registerTypes(moduleId);
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 2437979ef8..283bd40660 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -94,7 +94,8 @@ struct QQmlImportInstance
int *vmajor, int *vminor, QQmlType* type_return,
QString *base = nullptr, bool *typeRecursionDetected = nullptr,
QQmlType::RegistrationType = QQmlType::AnyRegistrationType,
- QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion) const;
+ QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion,
+ QList<QQmlError> *errors = nullptr) const;
};
class QQmlImportNamespace
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 93ec9421ed..9f2a96d5d9 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -93,8 +93,7 @@ void QQmlDelayedError::catchJavaScriptException(QV4::ExecutionEngine *engine)
QQmlJavaScriptExpression::QQmlJavaScriptExpression()
- : m_error(nullptr),
- m_context(nullptr),
+ : m_context(nullptr),
m_prevExpression(nullptr),
m_nextExpression(nullptr),
m_v4Function(nullptr)
@@ -247,6 +246,9 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst())
g->Delete();
+ if (!watcher.wasDeleted())
+ setTranslationsCaptured(capture.translationCaptured);
+
ep->propertyCapture = lastPropertyCapture;
return result->asReturnedValue();
@@ -392,7 +394,7 @@ QQmlDelayedError *QQmlJavaScriptExpression::delayedError()
{
if (!m_error)
m_error = new QQmlDelayedError;
- return m_error;
+ return m_error.data();
}
QV4::ReturnedValue
@@ -458,7 +460,7 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext,
setCompilationUnit(m_v4Function->compilationUnit);
}
-void QQmlJavaScriptExpression::setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit)
+void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
m_compilationUnit = compilationUnit;
}
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index 01af3b89ca..de3fba0774 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -162,7 +162,7 @@ protected:
}
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f);
- void setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit);
+ void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
// We store some flag bits in the following flag pointers.
// activeGuards:flag1 - notifyOnValueChanged
@@ -171,13 +171,17 @@ protected:
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(); }
+
private:
friend class QQmlContextData;
friend class QQmlPropertyCapture;
friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **);
friend class QQmlTranslationBinding;
- QQmlDelayedError *m_error;
+ // m_error:flag1 translationsCapturedDuringEvaluation
+ QFlagPointer<QQmlDelayedError> m_error;
QQmlContextData *m_context;
QQmlJavaScriptExpression **m_prevExpression;
@@ -208,12 +212,14 @@ public:
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 captureTranslation() { translationCaptured = true; }
QQmlEngine *engine;
QQmlJavaScriptExpression *expression;
QQmlJavaScriptExpression::DeleteWatcher *watcher;
QFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards;
QStringList *errorString;
+ bool translationCaptured = false;
};
QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e)
@@ -260,18 +266,17 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v)
bool QQmlJavaScriptExpression::hasError() const
{
- return m_error && m_error->isValid();
+ return !m_error.isNull() && m_error->isValid();
}
bool QQmlJavaScriptExpression::hasDelayedError() const
{
- return m_error;
+ return !m_error.isNull();
}
inline void QQmlJavaScriptExpression::clearError()
{
- if (m_error)
- delete m_error;
+ delete m_error.data();
m_error = nullptr;
}
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 3fbe3df2ab..7c35f73e6c 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -74,7 +74,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, i
Scope scope(engine);
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>());
+ Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
r->d()->object = object;
r->d()->propertyType = propType;
void *args[] = { &r->d()->property(), nullptr };
@@ -86,7 +86,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProp
{
Scope scope(engine);
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>());
+ Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
r->d()->object = prop.object;
r->d()->property() = prop;
r->d()->propertyType = propType;
@@ -102,54 +102,45 @@ QVariant QmlListWrapper::toVariant() const
}
-ReturnedValue QmlListWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QmlListWrapper>());
const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m);
QV4::ExecutionEngine *v4 = w->engine();
- if (name->equals(v4->id_length()) && !w->d()->object.isNull()) {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- return Primitive::fromUInt32(count).asReturnedValue();
- }
-
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return getIndexed(m, idx, hasProperty);
-
- return Object::get(m, name, hasProperty);
-}
-
-ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *hasProperty)
-{
- Q_UNUSED(hasProperty);
+ if (index < count && w->d()->property().at) {
+ if (hasProperty)
+ *hasProperty = true;
+ return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index));
+ }
- Q_ASSERT(m->as<QmlListWrapper>());
- const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m);
- QV4::ExecutionEngine *v4 = w->engine();
-
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- if (index < count && w->d()->property().at) {
if (hasProperty)
- *hasProperty = true;
- return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index));
+ *hasProperty = false;
+ return Primitive::undefinedValue().asReturnedValue();
+ } else if (id.isString()) {
+ if (id == v4->id_length()->propertyKey() && !w->d()->object.isNull()) {
+ quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
+ return Primitive::fromUInt32(count).asReturnedValue();
+ }
}
- if (hasProperty)
- *hasProperty = false;
- return Primitive::undefinedValue().asReturnedValue();
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
-bool QmlListWrapper::put(Managed *m, String *name, const Value &value)
+bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
// doesn't do anything. Should we throw?
Q_UNUSED(m);
- Q_UNUSED(name);
+ Q_UNUSED(id);
Q_UNUSED(value);
+ Q_UNUSED(receiver);
return false;
}
-void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
+void QmlListWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
{
name->setM(nullptr);
*index = UINT_MAX;
@@ -163,7 +154,7 @@ void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name
p->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), *index));
return;
}
- return QV4::Object::advanceIterator(m, it, name, index, p, attrs);
+ return QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attrs);
}
void PropertyListPrototype::init(ExecutionEngine *)
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index e02831c8d1..2d6a0880e3 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -93,10 +93,9 @@ struct Q_QML_EXPORT QmlListWrapper : Object
QVariant toVariant() const;
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ 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 void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
};
struct PropertyListPrototype : Object
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index b4f7092a22..42c72e0447 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -350,7 +350,7 @@ ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const QV4::FunctionObjec
if (argc != 0)
THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments");
- QV4::DatePrototype::timezoneUpdated();
+ QV4::DatePrototype::timezoneUpdated(scope.engine);
RETURN_UNDEFINED();
}
@@ -825,10 +825,10 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale)
{
QV4::Scope scope(v4);
QV4LocaleDataDeletable *d = localeV4Data(scope.engine);
- QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocObject<QQmlLocaleData>());
+ QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocate<QQmlLocaleData>());
*wrapper->d()->locale = locale;
QV4::ScopedObject p(scope, d->prototype.value());
- wrapper->setPrototype(p);
+ wrapper->setPrototypeOf(p);
return wrapper.asReturnedValue();
}
diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h
index 8341b1f555..859c36e11b 100644
--- a/src/qml/qml/qqmllocale_p.h
+++ b/src/qml/qml/qqmllocale_p.h
@@ -58,6 +58,8 @@
#include <private/qqmlglobal_p.h>
#include <private/qv4object_p.h>
+QT_REQUIRE_CONFIG(qml_locale);
+
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp
index 597fe458fa..b59a26e17e 100644
--- a/src/qml/qml/qqmlloggingcategory.cpp
+++ b/src/qml/qml/qqmlloggingcategory.cpp
@@ -59,6 +59,7 @@
LoggingCategory {
id: category
name: "com.qt.category"
+ defaultLogLevel: LoggingCategory.Warning
}
Component.onCompleted: {
@@ -84,6 +85,17 @@
\sa QLoggingCategory::categoryName()
*/
+/*!
+ \qmlproperty enumeration QtQml::LoggingCategory::defaultLogLevel
+ \since 5.12
+
+ Holds the default log level of the logging category. By default it is
+ created with the LoggingCategory.Debug log level.
+
+ \note This property needs to be set when declaring the LoggingCategory
+ and cannot be changed later.
+*/
+
QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent)
: QObject(parent)
, m_initialized(false)
@@ -99,6 +111,11 @@ QString QQmlLoggingCategory::name() const
return QString::fromUtf8(m_name);
}
+QQmlLoggingCategory::DefaultLogLevel QQmlLoggingCategory::defaultLogLevel() const
+{
+ return m_defaultLogLevel;
+}
+
QLoggingCategory *QQmlLoggingCategory::category() const
{
return m_category.data();
@@ -111,10 +128,25 @@ void QQmlLoggingCategory::classBegin()
void QQmlLoggingCategory::componentComplete()
{
m_initialized = true;
- if (m_name.isNull())
+ if (m_name.isNull()) {
qmlWarning(this) << QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !");
+ } else {
+ QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData(), QtMsgType(m_defaultLogLevel)));
+ m_category.swap(category);
+ }
}
+void QQmlLoggingCategory::setDefaultLogLevel(DefaultLogLevel defaultLogLevel)
+{
+ if (m_initialized) {
+ qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the Item is created");
+ return;
+ }
+
+ m_defaultLogLevel = defaultLogLevel;
+}
+
+
void QQmlLoggingCategory::setName(const QString &name)
{
if (m_initialized) {
@@ -123,8 +155,6 @@ void QQmlLoggingCategory::setName(const QString &name)
}
m_name = name.toUtf8();
- QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData()));
- m_category.swap(category);
}
#include "moc_qqmlloggingcategory_p.cpp"
diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h
index 544db1fe33..ece06e04b4 100644
--- a/src/qml/qml/qqmlloggingcategory_p.h
+++ b/src/qml/qml/qqmlloggingcategory_p.h
@@ -65,11 +65,23 @@ class QQmlLoggingCategory : public QObject, public QQmlParserStatus
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QString name READ name WRITE setName)
+ Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 1)
public:
+ enum DefaultLogLevel {
+ Debug = QtDebugMsg,
+ Info = QtInfoMsg,
+ Warning = QtWarningMsg,
+ Critical = QtCriticalMsg,
+ Fatal = QtFatalMsg
+ };
+ Q_ENUM(DefaultLogLevel);
+
QQmlLoggingCategory(QObject *parent = nullptr);
virtual ~QQmlLoggingCategory();
+ DefaultLogLevel defaultLogLevel() const;
+ void setDefaultLogLevel(DefaultLogLevel defaultLogLevel);
QString name() const;
void setName(const QString &name);
@@ -81,6 +93,7 @@ public:
private:
QByteArray m_name;
QScopedPointer<QLoggingCategory> m_category;
+ DefaultLogLevel m_defaultLogLevel = Debug;
bool m_initialized;
};
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 2705321e77..b31058ece5 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -288,7 +288,14 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
{
if (scriptCallback && scriptApi(e).isUndefined()) {
- setScriptApi(e, scriptCallback(e, e));
+ QJSValue value = scriptCallback(e, e);
+ if (value.isQObject()) {
+ QObject *o = value.toQObject();
+ // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
+ // should behave identically to QML singleton types.
+ e->setContextForObject(o, new QQmlContext(e->rootContext(), e));
+ }
+ setScriptApi(e, value);
} else if (qobjectCallback && !qobjectApi(e)) {
QObject *o = qobjectCallback(e, e);
setQObjectApi(e, o);
@@ -297,6 +304,9 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
}
// if this object can use a property cache, create it now
QQmlData::ensurePropertyCache(e, o);
+ // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
+ // should behave identically to QML singleton types.
+ e->setContextForObject(o, new QQmlContext(e->rootContext(), e));
} else if (!url.isEmpty() && !qobjectApi(e)) {
QQmlComponent component(e, url, QQmlComponent::PreferSynchronous);
QObject *o = component.beginCreate(e->rootContext());
@@ -600,7 +610,7 @@ QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
Q_ASSERT(isComposite());
if (!engine || !d)
return QQmlType();
- QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt);
+ QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return QQmlType();
QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
@@ -614,11 +624,11 @@ QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) c
Q_ASSERT(isComposite());
if (!engine)
return nullptr;
- QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt);
+ QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return nullptr;
QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
- return compilationUnit->rootPropertyCache();
+ return compilationUnit->rootPropertyCache().data();
}
static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
@@ -865,7 +875,7 @@ QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersio
{
for (int i = 0; i < propertyCaches.count(); ++i)
if (propertyCaches.at(i).minorVersion == minorVersion)
- return propertyCaches.at(i).cache;
+ return propertyCaches.at(i).cache.data();
return nullptr;
}
@@ -1877,6 +1887,23 @@ void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
p->maxMinorVersion = qMax(p->maxMinorVersion, versionMinor);
}
+//From qqml.h
+int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+{
+ QMutexLocker lock(metaTypeDataLock());
+ QQmlMetaTypeData *data = metaTypeData();
+
+ QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data);
+ if (!module)
+ return -1;
+
+ QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(qmlName)), versionMinor);
+ if (!type.isValid())
+ return -1;
+
+ return type.index();
+}
+
bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri, int majorVersion)
{
const QQmlMetaTypeData *data = metaTypeData();
@@ -2248,19 +2275,25 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStrin
}
/*!
- Returns the type (if any) that corresponds to the QVariant::Type \a userType.
- Returns null if no type is registered.
+ Returns the type (if any) that corresponds to \a typeId. Depending on \a category, the
+ \a typeId is interpreted either as QVariant::Type or as QML type id returned by one of the
+ qml type registration functions. Returns null if no type is registered.
*/
-QQmlType QQmlMetaType::qmlType(int userType)
+QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category)
{
QMutexLocker lock(metaTypeDataLock());
QQmlMetaTypeData *data = metaTypeData();
- QQmlTypePrivate *type = data->idToType.value(userType);
- if (type && type->typeId == userType)
- return QQmlType(type);
- else
- return QQmlType();
+ if (category == TypeIdCategory::MetaType) {
+ QQmlTypePrivate *type = data->idToType.value(typeId);
+ if (type && type->typeId == typeId)
+ return QQmlType(type);
+ } else if (category == TypeIdCategory::QmlType) {
+ QQmlType type = data->types.value(typeId);
+ if (type.isValid())
+ return type;
+ }
+ return QQmlType();
}
/*!
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index bcaf62d6ba..4a5e4ba266 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -91,11 +91,16 @@ public:
static QList<QQmlType> qmlSingletonTypes();
static QList<QQmlType> qmlAllTypes();
+ enum class TypeIdCategory {
+ MetaType,
+ QmlType
+ };
+
static QQmlType qmlType(const QString &qualifiedName, int, int);
static QQmlType qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int, int);
static QQmlType qmlType(const QMetaObject *);
static QQmlType qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor);
- static QQmlType qmlType(int);
+ static QQmlType qmlType(int typeId, TypeIdCategory category = TypeIdCategory::MetaType);
static QQmlType qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports = false);
static QQmlPropertyCache *propertyCache(const QMetaObject *metaObject);
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 5aaf79c9e5..fb05201010 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -71,7 +71,7 @@ struct ActiveOCRestorer
};
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext,
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext,
QQmlIncubatorPrivate *incubator)
: phase(Startup)
, compilationUnit(compilationUnit)
@@ -99,7 +99,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::Compil
}
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState)
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState)
: phase(Startup)
, compilationUnit(compilationUnit)
, resolvedTypes(compilationUnit->resolvedTypes)
@@ -193,8 +193,8 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
context->importedScripts.set(v4, scripts);
QV4::ScopedValue v(scope);
for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) {
- QQmlScriptData *s = compilationUnit->dependentScripts.at(i);
- scripts->putIndexed(i, (v = s->scriptValueForContext(context)));
+ QQmlRefPointer<QQmlScriptData> s = compilationUnit->dependentScripts.at(i);
+ scripts->put(i, (v = s->scriptValueForContext(context)));
}
} else if (sharedState->creationContext) {
context->importedScripts = sharedState->creationContext->importedScripts;
@@ -252,7 +252,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData::
Q_ASSERT(!sharedState->allJavaScriptObjects);
sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount);
- QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1));
+ QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
qSwap(_qmlContext, qmlContext);
@@ -312,7 +312,7 @@ bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
if (!sharedState->allJavaScriptObjects)
sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount);
- QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1));
+ QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
qSwap(_qmlContext, qmlContext);
@@ -876,7 +876,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
&& !_valueTypeProperty)
QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) {
QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex());
@@ -898,7 +898,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
targetProperty = _valueTypeProperty;
subprop = bindingProperty;
}
- if (binding->containsTranslations()) {
+ if (binding->isTranslationBinding()) {
qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context);
} else {
QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
@@ -1144,9 +1144,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
isComponent = true;
- QQmlComponent *component = new QQmlComponent(engine, compilationUnit, index, parent);
+ QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent);
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compilationUnit, obj, QStringLiteral("<component>"), context->url()));
+ compilationUnit.data(), obj, QStringLiteral("<component>"), context->url()));
QQmlComponentPrivate::get(component)->creationContext = context;
instance = component;
ddata = QQmlData::get(instance, /*create*/true);
@@ -1157,7 +1157,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QQmlType type = typeRef->type;
if (type.isValid()) {
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compilationUnit, obj, type.qmlTypeName(), context->url()));
+ compilationUnit.data(), obj, type.qmlTypeName(), context->url()));
void *ddataMemory = nullptr;
type.create(&instance, &ddataMemory, sizeof(QQmlData));
@@ -1190,8 +1190,8 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
} else {
Q_ASSERT(typeRef->compilationUnit);
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compilationUnit, obj, typeRef->compilationUnit->fileName(),
- context->url()));
+ compilationUnit.data(), obj, typeRef->compilationUnit->fileName(),
+ context->url()));
if (typeRef->compilationUnit->data->isSingleton())
{
recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex)));
@@ -1254,7 +1254,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) {
customParser->engine = QQmlEnginePrivate::get(engine);
- customParser->imports = compilationUnit->typeNameCache;
+ customParser->imports = compilationUnit->typeNameCache.data();
QList<const QV4::CompiledData::Binding *> bindings;
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index);
@@ -1264,7 +1264,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
bindings << binding;
}
}
- customParser->applyBindings(instance, compilationUnit, bindings);
+ customParser->applyBindings(instance, compilationUnit.data(), bindings);
customParser->engine = nullptr;
customParser->imports = (QQmlTypeNameCache*)nullptr;
@@ -1280,7 +1280,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (installPropertyCache) {
if (ddata->propertyCache)
ddata->propertyCache->release();;
- ddata->propertyCache = cache;
+ ddata->propertyCache = cache.data();
ddata->propertyCache->addref();
}
@@ -1292,7 +1292,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
++sharedState->allJavaScriptObjects;
QV4::Scope valueScope(v4);
- QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1));
+ QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
qSwap(_qmlContext, qmlContext);
@@ -1344,6 +1344,11 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru
data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex());
b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
QQmlPropertyData::DontRemoveBinding);
+ if (!b->isValueTypeProxy()) {
+ QQmlBinding *binding = static_cast<QQmlBinding*>(b.data());
+ if (!binding->hasError() && !binding->hasDependencies())
+ b->removeFromObject();
+ }
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
return nullptr;
@@ -1436,7 +1441,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
vmeMetaObject = new QQmlVMEMetaObject(v4, _qobject, cache, compilationUnit, _compiledObjectIndex);
if (_ddata->propertyCache)
_ddata->propertyCache->release();
- _ddata->propertyCache = cache;
+ _ddata->propertyCache = cache.data();
_ddata->propertyCache->addref();
scopeObjectProtector = _ddata->jsWrapper.value();
} else {
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 67a5bdd827..435b213341 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -85,7 +85,7 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
{
Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
public:
- QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
+ QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
~QQmlObjectCreator();
QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr);
@@ -104,7 +104,7 @@ public:
QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; }
private:
- QQmlObjectCreator(QQmlContextData *contextData, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
+ QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
void init(QQmlContextData *parentContext);
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index 1b44bbdda3..fc798a2c23 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -181,46 +181,75 @@ void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj)
class QQmlOpenMetaObjectPrivate
{
public:
- QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q)
- : q(_q), parent(nullptr), type(nullptr), cacheProperties(false) {}
+ QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, bool _autoCreate, QObject *obj)
+ : q(_q), object(obj), autoCreate(_autoCreate) {}
+
+ struct Property {
+ private:
+ QVariant m_value;
+ QPointer<QObject> qobjectTracker;
+ public:
+ bool valueSet = false;
+
+ QVariant value() const {
+ if (QMetaType::typeFlags(m_value.userType()) & QMetaType::PointerToQObject
+ && qobjectTracker.isNull())
+ return QVariant::fromValue<QObject*>(nullptr);
+ return m_value;
+ }
+ QVariant &valueRef() { return m_value; }
+ void setValue(const QVariant &v) {
+ m_value = v;
+ valueSet = true;
+ if (QMetaType::typeFlags(v.userType()) & QMetaType::PointerToQObject)
+ qobjectTracker = m_value.value<QObject*>();
+ }
+ };
- inline QPair<QVariant, bool> &getDataRef(int idx) {
- while (data.count() <= idx)
- data << QPair<QVariant, bool>(QVariant(), false);
- return data[idx];
+ inline void setPropertyValue(int idx, const QVariant &value) {
+ if (data.count() <= idx)
+ data.resize(idx + 1);
+ data[idx].setValue(value);
}
- inline QVariant &getData(int idx) {
- QPair<QVariant, bool> &prop = getDataRef(idx);
- if (!prop.second) {
- prop.first = q->initialValue(idx);
- prop.second = true;
- }
- return prop.first;
+ inline Property &propertyRef(int idx) {
+ if (data.count() <= idx)
+ data.resize(idx + 1);
+ Property &prop = data[idx];
+ if (!prop.valueSet)
+ prop.setValue(q->initialValue(idx));
+ return prop;
+ }
+
+ inline QVariant propertyValue(int idx) {
+ auto &prop = propertyRef(idx);
+ return prop.value();
+ }
+
+ inline QVariant &propertyValueRef(int idx) {
+ auto &prop = propertyRef(idx);
+ return prop.valueRef();
}
- inline bool hasData(int idx) const {
+ inline bool hasProperty(int idx) const {
if (idx >= data.count())
return false;
- return data[idx].second;
+ return data[idx].valueSet;
}
- bool autoCreate;
QQmlOpenMetaObject *q;
- QAbstractDynamicMetaObject *parent;
- QList<QPair<QVariant, bool> > data;
+ QAbstractDynamicMetaObject *parent = nullptr;
+ QVector<Property> data;
QObject *object;
- QQmlOpenMetaObjectType *type;
- bool cacheProperties;
+ QQmlRefPointer<QQmlOpenMetaObjectType> type;
+ bool autoCreate;
+ bool cacheProperties = false;
};
QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bool automatic)
-: d(new QQmlOpenMetaObjectPrivate(this))
+: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj))
{
- d->autoCreate = automatic;
- d->object = obj;
-
- d->type = new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr);
+ d->type.adopt(new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr));
d->type->d->referers.insert(this);
QObjectPrivate *op = QObjectPrivate::get(obj);
@@ -230,13 +259,9 @@ QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bo
}
QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, QQmlOpenMetaObjectType *type, bool automatic)
-: d(new QQmlOpenMetaObjectPrivate(this))
+: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj))
{
- d->autoCreate = automatic;
- d->object = obj;
-
d->type = type;
- d->type->addref();
d->type->d->referers.insert(this);
QObjectPrivate *op = QObjectPrivate::get(obj);
@@ -250,13 +275,12 @@ QQmlOpenMetaObject::~QQmlOpenMetaObject()
if (d->parent)
delete d->parent;
d->type->d->referers.remove(this);
- d->type->release();
delete d;
}
QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const
{
- return d->type;
+ return d->type.data();
}
void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName)
@@ -276,13 +300,11 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void *
int propId = id - d->type->d->propertyOffset;
if (c == QMetaObject::ReadProperty) {
propertyRead(propId);
- *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId);
+ *reinterpret_cast<QVariant *>(a[0]) = d->propertyValue(propId);
} else if (c == QMetaObject::WriteProperty) {
- if (propId >= d->data.count() || d->data.at(propId).first != *reinterpret_cast<QVariant *>(a[0])) {
+ if (propId >= d->data.count() || d->data.at(propId).value() != *reinterpret_cast<QVariant *>(a[0])) {
propertyWrite(propId);
- QPair<QVariant, bool> &prop = d->getDataRef(propId);
- prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]));
- prop.second = true;
+ d->setPropertyValue(propId, propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])));
propertyWritten(propId);
activate(o, d->type->d->signalOffset + propId, nullptr);
}
@@ -303,14 +325,12 @@ QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const
QVariant QQmlOpenMetaObject::value(int id) const
{
- return d->getData(id);
+ return d->propertyValue(id);
}
void QQmlOpenMetaObject::setValue(int id, const QVariant &value)
{
- QPair<QVariant, bool> &prop = d->getDataRef(id);
- prop.first = propertyWriteValue(id, value);
- prop.second = true;
+ d->setPropertyValue(id, propertyWriteValue(id, value));
activate(d->object, id + d->type->d->signalOffset, nullptr);
}
@@ -320,23 +340,18 @@ QVariant QQmlOpenMetaObject::value(const QByteArray &name) const
if (iter == d->type->d->names.cend())
return QVariant();
- return d->getData(*iter);
+ return d->propertyValue(*iter);
}
-QVariant &QQmlOpenMetaObject::operator[](const QByteArray &name)
+QVariant &QQmlOpenMetaObject::valueRef(const QByteArray &name)
{
QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
Q_ASSERT(iter != d->type->d->names.cend());
- return d->getData(*iter);
-}
-
-QVariant &QQmlOpenMetaObject::operator[](int id)
-{
- return d->getData(id);
+ return d->propertyValueRef(*iter);
}
-bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
+bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, bool force)
{
QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
@@ -348,11 +363,10 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
}
if (id >= 0) {
- QVariant &dataVal = d->getData(id);
- if (dataVal == val)
+ if (!force && d->propertyValue(id) == val)
return false;
- dataVal = val;
+ d->setPropertyValue(id, val);
activate(d->object, id + d->type->d->signalOffset, nullptr);
return true;
}
@@ -363,7 +377,7 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
// returns true if this value has been initialized by a call to either value() or setValue()
bool QQmlOpenMetaObject::hasValue(int id) const
{
- return d->hasData(id);
+ return d->hasProperty(id);
}
void QQmlOpenMetaObject::setCached(bool c)
diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h
index 4905190b75..168a2a6f7f 100644
--- a/src/qml/qml/qqmlopenmetaobject_p.h
+++ b/src/qml/qml/qqmlopenmetaobject_p.h
@@ -100,11 +100,10 @@ public:
~QQmlOpenMetaObject() override;
QVariant value(const QByteArray &) const;
- bool setValue(const QByteArray &, const QVariant &);
+ bool setValue(const QByteArray &, const QVariant &, bool force = false);
QVariant value(int) const;
void setValue(int, const QVariant &);
- QVariant &operator[](const QByteArray &);
- QVariant &operator[](int);
+ QVariant &valueRef(const QByteArray &);
bool hasValue(int) const;
int count() const;
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index c4487f91a3..bc0124eeab 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -255,7 +255,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
{
if (!obj) return;
- QQmlTypeNameCache *typeNameCache = context?context->imports:nullptr;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context?context->imports:nullptr;
QObject *currentObject = obj;
QVector<QStringRef> path;
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index b78a2ddd20..0785910cec 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -979,13 +979,13 @@ public:
void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); }
QQmlPropertyCache *at(int index) const { return data.at(index).data(); }
- void set(int index, QQmlPropertyCache *replacement) {
+ void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) {
if (QQmlPropertyCache *oldCache = data.at(index).data()) {
- if (replacement == oldCache)
+ if (replacement.data() == oldCache)
return;
oldCache->release();
}
- data[index] = replacement;
+ data[index] = replacement.data();
replacement->addref();
}
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index c656fac4ff..85167848fb 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -504,11 +504,12 @@ void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
if (!blob ||
blob->status() == Error || blob->status() == Complete ||
- status() == Error || status() == Complete || m_isDone ||
- m_waitingFor.contains(blob))
+ status() == Error || status() == Complete || m_isDone)
return;
- blob->addref();
+ for (auto existingDep: qAsConst(m_waitingFor))
+ if (existingDep.data() == blob)
+ return;
m_data.setStatus(WaitingForDependencies);
@@ -691,13 +692,11 @@ void QQmlDataBlob::tryDone()
void QQmlDataBlob::cancelAllWaitingFor()
{
while (m_waitingFor.count()) {
- QQmlDataBlob *blob = m_waitingFor.takeLast();
+ QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast();
Q_ASSERT(blob->m_waitingOnMe.contains(this));
blob->m_waitingOnMe.removeOne(this);
-
- blob->release();
}
}
@@ -706,7 +705,8 @@ void QQmlDataBlob::notifyAllWaitingOnMe()
while (m_waitingOnMe.count()) {
QQmlDataBlob *blob = m_waitingOnMe.takeLast();
- Q_ASSERT(blob->m_waitingFor.contains(this));
+ Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(),
+ [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; }));
blob->notifyComplete(this);
}
@@ -714,13 +714,19 @@ void QQmlDataBlob::notifyAllWaitingOnMe()
void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
{
- Q_ASSERT(m_waitingFor.contains(blob));
Q_ASSERT(blob->status() == Error || blob->status() == Complete);
QQmlCompilingProfiler prof(typeLoader()->profiler(), blob);
m_inCallback = true;
- m_waitingFor.removeOne(blob);
+ QQmlRefPointer<QQmlDataBlob> blobRef;
+ for (int i = 0; i < m_waitingFor.count(); ++i) {
+ if (m_waitingFor.at(i).data() == blob) {
+ blobRef = m_waitingFor.takeAt(i);
+ break;
+ }
+ }
+ Q_ASSERT(blobRef);
if (blob->status() == Error) {
dependencyError(blob);
@@ -728,8 +734,6 @@ void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
dependencyComplete(blob);
}
- blob->release();
-
if (!isError() && m_waitingFor.isEmpty())
allDependenciesDone();
@@ -1327,20 +1331,17 @@ QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoa
QQmlTypeLoader::Blob::~Blob()
{
- for (int ii = 0; ii < m_qmldirs.count(); ++ii)
- m_qmldirs.at(ii)->release();
}
bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors)
{
- QQmlQmldirData *data = typeLoader()->getQmldir(url);
+ QQmlRefPointer<QQmlQmldirData> data = typeLoader()->getQmldir(url);
data->setImport(this, import);
data->setPriority(this, priority);
if (data->status() == Error) {
// This qmldir must not exist - which is not an error
- data->release();
return true;
} else if (data->status() == Complete) {
// This data is already available
@@ -1348,11 +1349,11 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData:
}
// Wait for this data to become available
- addDependency(data);
+ addDependency(data.data());
return true;
}
-bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors)
+bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors)
{
QString qmldirIdentifier = data->urlString();
QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1);
@@ -1378,8 +1379,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile
const auto qmldirScripts = qmldir.scripts();
for (const QQmlDirParser::Script &script : qmldirScripts) {
QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
- QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
+ addDependency(blob.data());
scriptImported(blob, import->location, script.nameSpace, importQualifier);
}
@@ -1398,8 +1399,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
const QString &importQualifier = stringAt(import->qualifierIndex);
if (import->type == QV4::CompiledData::Import::ImportScript) {
QUrl scriptUrl = finalUrl().resolved(QUrl(importUri));
- QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
+ addDependency(blob.data());
scriptImported(blob, import->location, importQualifier, QString());
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
@@ -1426,8 +1427,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
const auto qmldirScripts = qmldir.scripts();
for (const QQmlDirParser::Script &script : qmldirScripts) {
QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
- QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
+ addDependency(blob.data());
scriptImported(blob, import->location, script.nameSpace, importQualifier);
}
@@ -1499,14 +1500,6 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
return true;
}
-void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob)
-{
- if (blob->type() == QQmlDataBlob::QmldirFile) {
- QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
- data->release();
- }
-}
-
void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
{
if (blob->type() == QQmlDataBlob::QmldirFile) {
@@ -1532,7 +1525,7 @@ bool QQmlTypeLoader::Blob::isDebugging() const
return typeLoader()->engine()->handle()->debugger() != nullptr;
}
-bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors)
+bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors)
{
bool resolve = true;
@@ -1552,7 +1545,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE
if (resolve) {
// This is the (current) best resolution for this import
if (!updateQmldir(data, import, errors)) {
- data->release();
return false;
}
@@ -1562,7 +1554,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE
}
}
- data->release();
return true;
}
@@ -1664,7 +1655,7 @@ QUrl QQmlTypeLoader::normalize(const QUrl &unNormalizedUrl)
/*!
Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached.
*/
-QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
+QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
{
Q_ASSERT(!unNormalizedUrl.isRelative() &&
(QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() ||
@@ -1707,8 +1698,6 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
}
}
- typeData->addref();
-
return typeData;
}
@@ -1716,20 +1705,20 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
Returns a QQmlTypeData for the given \a data with the provided base \a url. The
QQmlTypeData will not be cached.
*/
-QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode)
+QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode)
{
LockHolder<QQmlTypeLoader> holder(this);
QQmlTypeData *typeData = new QQmlTypeData(url, this);
QQmlTypeLoader::loadWithStaticData(typeData, data, mode);
- return typeData;
+ return QQmlRefPointer<QQmlTypeData>(typeData, QQmlRefPointer<QQmlTypeData>::Adopt);
}
/*!
Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
*/
-QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl)
+QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl)
{
Q_ASSERT(!unNormalizedUrl.isRelative() &&
(QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() ||
@@ -1754,15 +1743,13 @@ QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl)
}
}
- scriptBlob->addref();
-
return scriptBlob;
}
/*!
Returns a QQmlQmldirData for \a url. The QQmlQmldirData may be cached.
*/
-QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
+QQmlRefPointer<QQmlQmldirData> QQmlTypeLoader::getQmldir(const QUrl &url)
{
Q_ASSERT(!url.isRelative() &&
(QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
@@ -1778,8 +1765,6 @@ QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
QQmlTypeLoader::load(qmldirData);
}
- qmldirData->addref();
-
return qmldirData;
}
@@ -2073,17 +2058,9 @@ QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
QQmlTypeData::~QQmlTypeData()
{
- for (int ii = 0; ii < m_scripts.count(); ++ii)
- m_scripts.at(ii).script->release();
- for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) {
- if (QQmlTypeData *tdata = m_compositeSingletons.at(ii).typeData)
- tdata->release();
- }
- for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
- it != end; ++it) {
- if (QQmlTypeData *tdata = it->typeData)
- tdata->release();
- }
+ m_scripts.clear();
+ m_compositeSingletons.clear();
+ m_resolvedTypes.clear();
}
const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
@@ -2201,7 +2178,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName
{
QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches,
&pendingGroupPropertyBindings,
- engine, m_compiledData, &m_importCache);
+ engine, m_compiledData.data(), &m_importCache);
QQmlCompileError error = propertyCacheCreator.buildMetaObjects();
if (error.isSet()) {
setError(error);
@@ -2209,7 +2186,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName
}
}
- QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData);
+ QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData.data());
aliasCreator.appendAliasPropertiesToMetaObjects();
pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches);
@@ -2393,8 +2370,7 @@ void QQmlTypeData::done()
}
m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
- QQmlScriptData *scriptData = script.script->scriptData();
- scriptData->addref();
+ QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
m_compiledData->dependentScripts << scriptData;
}
}
@@ -2637,8 +2613,8 @@ void QQmlTypeData::resolveTypes()
// Add any imported scripts to our resolved set
const auto resolvedScripts = m_importCache.resolvedScripts();
for (const QQmlImports::ScriptReference &script : resolvedScripts) {
- QQmlScriptBlob *blob = typeLoader()->getScript(script.location);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location);
+ addDependency(blob.data());
ScriptReference ref;
//ref.location = ...
@@ -2681,7 +2657,7 @@ void QQmlTypeData::resolveTypes()
// TODO: give an error message? If so, we should record and show the path of the cycle.
continue;
}
- addDependency(ref.typeData);
+ addDependency(ref.typeData.data());
ref.prefix = csRef.prefix;
m_compositeSingletons << ref;
@@ -2711,7 +2687,7 @@ void QQmlTypeData::resolveTypes()
if (ref.type.isComposite()) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
- addDependency(ref.typeData);
+ addDependency(ref.typeData.data());
}
ref.majorVersion = majorVersion;
ref.minorVersion = minorVersion;
@@ -2743,7 +2719,7 @@ QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(
for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
(*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix);
- m_importCache.populateCache(*typeNameCache);
+ m_importCache.populateCache(typeNameCache->data());
QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
@@ -2833,7 +2809,7 @@ bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &
return true;
}
-void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
+void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
{
ScriptReference ref;
ref.script = blob;
@@ -2921,7 +2897,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
}
QV4::ScopedValue v(scope);
for (int ii = 0; ii < scripts.count(); ++ii)
- scriptsArray->putIndexed(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(ctxt)));
+ scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(ctxt)));
if (!hasEngine())
initialize(parentCtxt->engine);
@@ -2959,8 +2935,6 @@ void QQmlScriptData::clear()
typeNameCache = nullptr;
}
- for (int ii = 0; ii < scripts.count(); ++ii)
- scripts.at(ii)->release();
scripts.clear();
// An addref() was made when the QQmlCleanup was added to the engine.
@@ -2968,19 +2942,15 @@ void QQmlScriptData::clear()
}
QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
-: QQmlTypeLoader::Blob(url, JavaScriptFile, loader), m_scriptData(nullptr)
+: QQmlTypeLoader::Blob(url, JavaScriptFile, loader)
{
}
QQmlScriptBlob::~QQmlScriptBlob()
{
- if (m_scriptData) {
- m_scriptData->release();
- m_scriptData = nullptr;
- }
}
-QQmlScriptData *QQmlScriptBlob::scriptData() const
+QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const
{
return m_scriptData;
}
@@ -3017,11 +2987,12 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
}
QmlIR::ScriptDirectivesCollector collector(&irUnit);
+ irUnit.jsParserEngine.setDirectives(&collector);
QList<QQmlError> errors;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(
- &irUnit.jsModule, &irUnit.jsGenerator, urlString(), finalUrlString(),
- source, &errors, &collector);
+ &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(),
+ source, &errors);
// No need to addref on unit, it's initial refcount is 1
source.clear();
if (!errors.isEmpty()) {
@@ -3095,6 +3066,7 @@ void QQmlScriptBlob::done()
}
m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
}
+ m_scripts.clear();
m_importCache.populateCache(m_scriptData->typeNameCache);
}
@@ -3104,7 +3076,7 @@ QString QQmlScriptBlob::stringAt(int index) const
return m_scriptData->m_precompiledScript->data->stringAt(index);
}
-void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace)
+void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace)
{
ScriptReference ref;
ref.script = blob;
@@ -3118,7 +3090,7 @@ void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledDat
void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
{
Q_ASSERT(!m_scriptData);
- m_scriptData = new QQmlScriptData();
+ m_scriptData.adopt(new QQmlScriptData());
m_scriptData->url = finalUrl();
m_scriptData->urlString = finalUrlString();
m_scriptData->m_precompiledScript = unit;
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index e75719866d..98ac4b1bc1 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -217,7 +217,7 @@ private:
QList<QQmlDataBlob *> m_waitingOnMe;
// List of QQmlDataBlob's that I am waiting for to complete.
- QList<QQmlDataBlob *> m_waitingFor;
+ QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor;
int m_redirectCount:30;
bool m_inCallback:1;
@@ -279,14 +279,13 @@ public:
bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors);
- bool updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
+ bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
private:
- virtual bool qmldirDataAvailable(QQmlQmldirData *, QList<QQmlError> *);
+ virtual bool qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &, QList<QQmlError> *);
- virtual void scriptImported(QQmlScriptBlob *, const QV4::CompiledData::Location &, const QString &, const QString &) {}
+ virtual void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &, const QV4::CompiledData::Location &, const QString &, const QString &) {}
- void dependencyError(QQmlDataBlob *) override;
void dependencyComplete(QQmlDataBlob *) override;
protected:
@@ -296,7 +295,7 @@ public:
QQmlImports m_importCache;
QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports;
- QList<QQmlQmldirData *> m_qmldirs;
+ QVector<QQmlRefPointer<QQmlQmldirData>> m_qmldirs;
QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError;
};
@@ -307,11 +306,11 @@ public:
static QUrl normalize(const QUrl &unNormalizedUrl);
- QQmlTypeData *getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous);
- QQmlTypeData *getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous);
+ QQmlRefPointer<QQmlTypeData> getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous);
+ QQmlRefPointer<QQmlTypeData> getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous);
- QQmlScriptBlob *getScript(const QUrl &unNormalizedUrl);
- QQmlQmldirData *getQmldir(const QUrl &);
+ QQmlRefPointer<QQmlScriptBlob> getScript(const QUrl &unNormalizedUrl);
+ QQmlRefPointer<QQmlQmldirData> getQmldir(const QUrl &);
QString absoluteFilePath(const QString &path);
bool directoryExists(const QString &path);
@@ -424,13 +423,13 @@ class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob
public:
struct TypeReference
{
- TypeReference() : majorVersion(0), minorVersion(0), typeData(nullptr), needsCreation(true) {}
+ TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {}
QV4::CompiledData::Location location;
QQmlType type;
int majorVersion;
int minorVersion;
- QQmlTypeData *typeData;
+ QQmlRefPointer<QQmlTypeData> typeData;
QString prefix; // used by CompositeSingleton types
QString qualifiedName() const;
bool needsCreation;
@@ -438,11 +437,9 @@ public:
struct ScriptReference
{
- ScriptReference() : script(nullptr) {}
-
QV4::CompiledData::Location location;
QString qualifier;
- QQmlScriptBlob *script;
+ QQmlRefPointer<QQmlScriptBlob> script;
};
private:
@@ -495,7 +492,7 @@ private:
bool reportErrors = true,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType);
- void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+ void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
SourceCodeData m_backupSourceCode; // used when cache verification fails.
@@ -541,7 +538,7 @@ public:
QUrl url;
QString urlString;
QQmlTypeNameCache *typeNameCache;
- QList<QQmlScriptBlob *> scripts;
+ QVector<QQmlRefPointer<QQmlScriptBlob>> scripts;
QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt);
@@ -571,15 +568,13 @@ public:
struct ScriptReference
{
- ScriptReference() : script(nullptr) {}
-
QV4::CompiledData::Location location;
QString qualifier;
QString nameSpace;
- QQmlScriptBlob *script;
+ QQmlRefPointer<QQmlScriptBlob> script;
};
- QQmlScriptData *scriptData() const;
+ QQmlRefPointer<QQmlScriptData> scriptData() const;
protected:
void dataReceived(const SourceCodeData &) override;
@@ -589,11 +584,11 @@ protected:
QString stringAt(int index) const override;
private:
- void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+ void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
void initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit);
QList<ScriptReference> m_scripts;
- QQmlScriptData *m_scriptData;
+ QQmlRefPointer<QQmlScriptData> m_scriptData;
};
class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index ce35e966aa..c6affcb79c 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -95,12 +95,17 @@ QObject* QQmlTypeWrapper::singletonObject() const
QVariant QQmlTypeWrapper::toVariant() const
{
- QObject *qobjectSingleton = singletonObject();
- if (qobjectSingleton)
+ // Only Singleton type wrappers can be converted to a variant.
+ if (!isSingleton())
+ return QVariant();
+
+ QQmlEngine *e = engine()->qmlEngine();
+ QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo();
+ siinfo->init(e);
+ if (QObject *qobjectSingleton = siinfo->qobjectApi(e))
return QVariant::fromValue<QObject*>(qobjectSingleton);
- // only QObject Singleton Type can be converted to a variant.
- return QVariant();
+ return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e));
}
@@ -111,7 +116,7 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o,
Q_ASSERT(t.isValid());
Scope scope(engine);
- Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QQmlTypeWrapper>());
+ Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>());
w->d()->mode = mode; w->d()->object = o;
w->d()->typePrivate = t.priv();
QQmlType::refHandle(w->d()->typePrivate);
@@ -120,15 +125,15 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o,
// Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a
// namespace.
-ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, QQmlTypeNameCache *t, const QQmlImportRef *importNamespace,
+ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace,
Heap::QQmlTypeWrapper::TypeNameMode mode)
{
Q_ASSERT(t);
Q_ASSERT(importNamespace);
Scope scope(engine);
- Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QQmlTypeWrapper>());
- w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t; w->d()->importNamespace = importNamespace;
+ Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>());
+ w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace;
t->addref();
return w.asReturnedValue();
}
@@ -162,12 +167,16 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n
return v4->throwTypeError(message);
}
-ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlTypeWrapper>());
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine();
QV4::Scope scope(v4);
+ ScopedString name(scope, id.asStringOrSymbol());
Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(m));
@@ -232,7 +241,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
if (ok) {
- Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocObject<QQmlScopedEnumWrapper>());
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
enumWrapper->d()->typePrivate = type.priv();
QQmlType::refHandle(enumWrapper->d()->typePrivate);
enumWrapper->d()->scopeEnumIndex = value;
@@ -263,7 +272,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
return create(scope.engine, object, r.type, w->d()->mode);
} else if (r.scriptIndex != -1) {
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
- return scripts->getIndexed(r.scriptIndex);
+ return scripts->get(r.scriptIndex);
} else if (r.importNamespace) {
return create(scope.engine, object, context->imports, r.importNamespace);
}
@@ -279,7 +288,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
}
bool ok = false;
- const ReturnedValue result = Object::get(m, name, &ok);
+ const ReturnedValue result = Object::virtualGet(m, id, receiver, &ok);
if (hasProperty)
*hasProperty = ok;
@@ -295,16 +304,20 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
}
-bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
+bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+
+
Q_ASSERT(m->as<QQmlTypeWrapper>());
QQmlTypeWrapper *w = static_cast<QQmlTypeWrapper *>(m);
- QV4::ExecutionEngine *v4 = w->engine();
- if (v4->hasException)
+ QV4::Scope scope(w);
+ if (scope.engine->hasException)
return false;
- QV4::Scope scope(v4);
- QQmlContextData *context = v4->callingQmlContext();
+ ScopedString name(scope, id.asStringOrSymbol());
+ QQmlContextData *context = scope.engine->callingQmlContext();
QQmlType type = w->d()->type();
if (type.isValid() && !type.isSingleton() && w->d()->object) {
@@ -312,7 +325,7 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
QQmlEngine *e = scope.engine->qmlEngine();
QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(e)), object);
if (ao)
- return QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value);
+ return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value);
return false;
} else if (type.isSingleton()) {
QQmlEngine *e = scope.engine->qmlEngine();
@@ -321,12 +334,12 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
QObject *qobjectSingleton = siinfo->qobjectApi(e);
if (qobjectSingleton) {
- return QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value);
+ return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value);
} else if (!siinfo->scriptApi(e).isUndefined()) {
- QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e)));
+ QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, siinfo->scriptApi(e)));
if (!apiprivate) {
QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"');
- v4->throwError(error);
+ scope.engine->throwError(error);
return false;
} else {
return apiprivate->put(name, value);
@@ -337,15 +350,21 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
return false;
}
-PropertyAttributes QQmlTypeWrapper::query(const Managed *m, String *name)
+PropertyAttributes QQmlTypeWrapper::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
{
- // ### Implement more efficiently.
- bool hasProperty = false;
- static_cast<Object *>(const_cast<Managed*>(m))->get(name, &hasProperty);
- return hasProperty ? Attr_Data : Attr_Invalid;
+ if (id.isString()) {
+ Scope scope(m);
+ ScopedString n(scope, id.asStringOrSymbol());
+ // ### Implement more efficiently.
+ bool hasProperty = false;
+ static_cast<Object *>(m)->get(n, &hasProperty);
+ return hasProperty ? Attr_Data : Attr_Invalid;
+ }
+
+ return QV4::Object::virtualGetOwnProperty(m, id, p);
}
-bool QQmlTypeWrapper::isEqualTo(Managed *a, Managed *b)
+bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QV4::QQmlTypeWrapper>());
QV4::QQmlTypeWrapper *qmlTypeWrapperA = static_cast<QV4::QQmlTypeWrapper *>(a);
@@ -357,7 +376,7 @@ bool QQmlTypeWrapper::isEqualTo(Managed *a, Managed *b)
return false;
}
-ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value &var)
+ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var)
{
Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
@@ -385,10 +404,9 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value
if (!theirDData->compilationUnit)
return Encode(false);
- QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
+ QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
CompiledData::CompilationUnit *cu = td->compilationUnit();
myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId);
- td->release();
} else {
myQmlType = qenginepriv->metaObjectForType(myTypeId);
}
@@ -410,12 +428,16 @@ QQmlType Heap::QQmlScopedEnumWrapper::type() const
return QQmlType(typePrivate);
}
-ReturnedValue QQmlScopedEnumWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlScopedEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlScopedEnumWrapper>());
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QQmlScopedEnumWrapper *resource = static_cast<const QQmlScopedEnumWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
+ ScopedString name(scope, id.asStringOrSymbol());
QQmlType type = resource->d()->type();
int index = resource->d()->scopeEnumIndex;
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index 25ff7ba7c8..c0eb534d36 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -108,15 +108,15 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlType &,
Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums);
- static ReturnedValue create(ExecutionEngine *, QObject *, QQmlTypeNameCache *, const QQmlImportRef *,
+ static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *,
Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums);
-
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static PropertyAttributes query(const Managed *, String *name);
- static bool isEqualTo(Managed *that, Managed *o);
- static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
+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);
+ static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
+ static bool virtualIsEqualTo(Managed *that, Managed *o);
+ static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
};
struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object
@@ -124,7 +124,7 @@ struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object
V4_OBJECT2(QQmlScopedEnumWrapper, Object)
V4_NEEDS_DESTROY
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
};
}
diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
index ba0d305bd9..35b54c339b 100644
--- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h
+++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
@@ -55,7 +55,7 @@
QT_BEGIN_NAMESPACE
-class QQmlValueTypeProxyBinding : public QQmlAbstractBinding
+class Q_AUTOTEST_EXPORT QQmlValueTypeProxyBinding : public QQmlAbstractBinding
{
public:
QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex coreIndex);
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 6196a09d94..1fde5945ba 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -39,7 +39,6 @@
#include "qqmlvaluetypewrapper_p.h"
#include <private/qv8engine_p.h>
-
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlglobal_p.h>
@@ -49,6 +48,7 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4variantobject_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qv4stackframe_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <QtCore/qloggingcategory.h>
@@ -186,7 +186,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj
Scope scope(engine);
initProto(engine);
- Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocObject<QQmlValueTypeReference>());
+ Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>());
r->d()->object = object;
r->d()->property = property;
r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
@@ -200,7 +200,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria
Scope scope(engine);
initProto(engine);
- Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocObject<QQmlValueTypeWrapper>());
+ Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>());
r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
r->d()->valueType = QQmlValueTypeFactory::valueType(typeId);
r->d()->gadgetPtr = nullptr;
@@ -227,7 +227,7 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const
return true;
}
-bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other)
+bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other)
{
Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other);
QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m);
@@ -241,16 +241,20 @@ bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other)
return false;
}
-PropertyAttributes QQmlValueTypeWrapper::query(const Managed *m, String *name)
+PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p)
{
- Q_ASSERT(m->as<const QQmlValueTypeWrapper>());
- const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
+ if (id.isString()) {
+ Scope scope(m);
+ ScopedString n(scope, id.asStringOrSymbol());
+ const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
+ QQmlPropertyData *result = r->d()->propertyCache()->property(n.getPointer(), nullptr, nullptr);
+ return result ? Attr_Data : Attr_Invalid;
+ }
- QQmlPropertyData *result = r->d()->propertyCache()->property(name, nullptr, nullptr);
- return result ? Attr_Data : Attr_Invalid;
+ return QV4::Object::virtualGetOwnProperty(m, id, p);
}
-void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+void QQmlValueTypeWrapper::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{
name->setM(nullptr);
*index = UINT_MAX;
@@ -275,7 +279,7 @@ void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value
return;
}
}
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+ QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
@@ -355,11 +359,17 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
return Encode(b->engine()->newString(result));
}
-ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
+
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
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>()) {
@@ -367,9 +377,9 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha
return Primitive::undefinedValue().asReturnedValue();
}
- QQmlPropertyData *result = r->d()->propertyCache()->property(name, nullptr, nullptr);
+ QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
if (!result)
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
if (hasProperty)
*hasProperty = true;
@@ -413,8 +423,11 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha
#undef VALUE_TYPE_ACCESSOR
}
-bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
+bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
Scope scope(v4);
@@ -435,8 +448,10 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
writeBackPropertyType = writebackProperty.userType();
}
+ ScopedString name(scope, id.asStringOrSymbol());
+
const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
- const QQmlPropertyData *pd = r->d()->propertyCache()->property(name, nullptr, nullptr);
+ const QQmlPropertyData *pd = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
if (!pd)
return false;
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index f99d207d68..ff8ab98bb6 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -106,11 +106,11 @@ public:
int typeId() const;
bool write(QObject *target, int propertyIndex) const;
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static bool isEqualTo(Managed *m, Managed *other);
- static PropertyAttributes query(const Managed *, String *name);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ 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 bool virtualIsEqualTo(Managed *m, Managed *other);
+ static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
+ static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h
index 9a94ac6258..13c5524d96 100644
--- a/src/qml/qml/qqmlvme_p.h
+++ b/src/qml/qml/qqmlvme_p.h
@@ -124,6 +124,10 @@ private:
// Used to check that a QQmlVME that is interrupted mid-execution
// is still valid. Checks all the objects and contexts have not been
// deleted.
+//
+// VME stands for Virtual Machine Execution. QML files used to
+// be compiled to a byte code data structure that a virtual machine executed
+// (for constructing the tree of QObjects and setting properties).
class QQmlVMEGuard
{
public:
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index c1d3980b58..6bc0f0aa1e 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -106,7 +106,7 @@ void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *)
QV4::Scope scope(v4);
QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value());
if (sp) {
- QV4::MemberData::Index index{ sp->d(), sp->d()->values.values + m_index };
+ QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + m_index };
index.set(v4, QV4::Primitive::nullValue());
}
}
@@ -176,7 +176,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
}
-QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache)
+QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache)
: object(obj),
cache(cache),
interceptors(nullptr),
@@ -316,7 +316,7 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje
QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine,
QObject *obj,
- QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId)
+ const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId)
: QQmlInterceptorMetaObject(obj, cache),
engine(engine),
ctxt(QQmlData::get(obj, true)->outerContext),
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 0c82686d47..dbcc9d2884 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -94,7 +94,7 @@ public:
class Q_QML_PRIVATE_EXPORT QQmlInterceptorMetaObject : public QAbstractDynamicMetaObject
{
public:
- QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache);
+ QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache);
~QQmlInterceptorMetaObject() override;
void registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor);
@@ -104,7 +104,7 @@ public:
QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o) override;
// Used by auto-tests for inspection
- QQmlPropertyCache *propertyCache() const { return cache; }
+ QQmlPropertyCache *propertyCache() const { return cache.data(); }
bool intercepts(QQmlPropertyIndex propertyIndex) const
{
@@ -146,7 +146,7 @@ class QQmlVMEMetaObjectEndpoint;
class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
{
public:
- QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId);
+ QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId);
~QQmlVMEMetaObject() override;
bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const;
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 567d83f3ee..51e5be4b5b 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -228,8 +228,7 @@ public:
static ReturnedValue create(ExecutionEngine *, NodeImpl *, const QList<NodeImpl *> &);
// JS API
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
};
void Heap::NamedNodeMap::init(NodeImpl *data, const QList<NodeImpl *> &list)
@@ -250,8 +249,7 @@ public:
V4_NEEDS_DESTROY
// JS API
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
// C++ API
static ReturnedValue create(ExecutionEngine *, NodeImpl *);
@@ -595,7 +593,7 @@ ReturnedValue NodePrototype::getProto(ExecutionEngine *v4)
Scope scope(v4);
QQmlXMLHttpRequestData *d = xhrdata(v4);
if (d->nodePrototype.isUndefined()) {
- ScopedObject p(scope, v4->memoryManager->allocObject<NodePrototype>());
+ ScopedObject p(scope, v4->memoryManager->allocate<NodePrototype>());
d->nodePrototype.set(v4, p);
v4->v8Engine->freezeObject(p);
}
@@ -606,12 +604,12 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data)
{
Scope scope(v4);
- Scoped<Node> instance(scope, v4->memoryManager->allocObject<Node>(data));
+ Scoped<Node> instance(scope, v4->memoryManager->allocate<Node>(data));
ScopedObject p(scope);
switch (data->type) {
case NodeImpl::Attr:
- instance->setPrototype((p = Attr::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Attr::prototype(v4)));
break;
case NodeImpl::Comment:
case NodeImpl::Document:
@@ -623,13 +621,13 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data)
case NodeImpl::ProcessingInstruction:
return Encode::undefined();
case NodeImpl::CDATA:
- instance->setPrototype((p = CDATA::prototype(v4)));
+ instance->setPrototypeUnchecked((p = CDATA::prototype(v4)));
break;
case NodeImpl::Text:
- instance->setPrototype((p = Text::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Text::prototype(v4)));
break;
case NodeImpl::Element:
- instance->setPrototype((p = Element::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Element::prototype(v4)));
break;
}
@@ -643,7 +641,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine)
Scope scope(engine);
ScopedObject p(scope, engine->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(engine)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr);
d->elementPrototype.set(engine, p);
engine->v8Engine->freezeObject(p);
@@ -658,7 +656,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine)
Scope scope(engine);
ScopedObject p(scope, engine->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(engine)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
p->defineAccessorProperty(QStringLiteral("name"), method_name, nullptr);
p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr);
p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr);
@@ -715,7 +713,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(v4)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr);
p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr);
d->characterDataPrototype.set(v4, p);
@@ -751,7 +749,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = CharacterData::prototype(v4)));
+ p->setPrototypeUnchecked((pp = CharacterData::prototype(v4)));
p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr);
p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr);
d->textPrototype.set(v4, p);
@@ -768,7 +766,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = Text::prototype(v4)));
+ p->setPrototypeUnchecked((pp = Text::prototype(v4)));
d->cdataPrototype.set(v4, p);
v4->v8Engine->freezeObject(p);
}
@@ -782,7 +780,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(v4)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
p->defineAccessorProperty(QStringLiteral("xmlVersion"), method_xmlVersion, nullptr);
p->defineAccessorProperty(QStringLiteral("xmlEncoding"), method_xmlEncoding, nullptr);
p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr);
@@ -876,10 +874,10 @@ ReturnedValue Document::load(ExecutionEngine *v4, const QByteArray &data)
return Encode::null();
}
- ScopedObject instance(scope, v4->memoryManager->allocObject<Node>(document));
+ ScopedObject instance(scope, v4->memoryManager->allocate<Node>(document));
document->release(); // the GC should own the NodeImpl via Node now
ScopedObject p(scope);
- instance->setPrototype((p = Document::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Document::prototype(v4)));
return instance.asReturnedValue();
}
@@ -888,33 +886,33 @@ bool Node::isNull() const
return d()->d == nullptr;
}
-ReturnedValue NamedNodeMap::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue NamedNodeMap::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<NamedNodeMap>());
+
const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- if ((int)index < r->d()->list().count()) {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+
+ if ((int)index < r->d()->list().count()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Node::create(v4, r->d()->list().at(index));
+ }
if (hasProperty)
- *hasProperty = true;
- return Node::create(v4, r->d()->list().at(index));
+ *hasProperty = false;
+ return Encode::undefined();
}
- if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
-}
-ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasProperty)
-{
- Q_ASSERT(m->as<NamedNodeMap>());
- const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m);
- QV4::ExecutionEngine *v4 = r->engine();
+ if (id.isSymbol())
+ return Object::virtualGet(m, id, receiver, hasProperty);
- name->makeIdentifier();
- if (name->equals(v4->id_length()))
+ if (id == v4->id_length()->propertyKey())
return Primitive::fromInt32(r->d()->list().count()).asReturnedValue();
- QString str = name->toQString();
+ QString str = id.toQString();
for (int ii = 0; ii < r->d()->list().count(); ++ii) {
if (r->d()->list().at(ii)->name == str) {
if (hasProperty)
@@ -930,41 +928,35 @@ ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasPropert
ReturnedValue NamedNodeMap::create(ExecutionEngine *v4, NodeImpl *data, const QList<NodeImpl *> &list)
{
- return (v4->memoryManager->allocObject<NamedNodeMap>(data, list))->asReturnedValue();
+ return (v4->memoryManager->allocate<NamedNodeMap>(data, list))->asReturnedValue();
}
-ReturnedValue NodeList::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue NodeList::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<NodeList>());
const NodeList *r = static_cast<const NodeList *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- if ((int)index < r->d()->d->children.count()) {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ if ((int)index < r->d()->d->children.count()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Node::create(v4, r->d()->d->children.at(index));
+ }
if (hasProperty)
- *hasProperty = true;
- return Node::create(v4, r->d()->d->children.at(index));
+ *hasProperty = false;
+ return Encode::undefined();
}
- if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
-}
-
-ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty)
-{
- Q_ASSERT(m->as<NodeList>());
- const NodeList *r = static_cast<const NodeList *>(m);
- QV4::ExecutionEngine *v4 = r->engine();
-
- name->makeIdentifier();
- if (name->equals(v4->id_length()))
+ if (id == v4->id_length()->propertyKey())
return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue();
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data)
{
- return (v4->memoryManager->allocObject<NodeList>(data))->asReturnedValue();
+ return (v4->memoryManager->allocate<NodeList>(data))->asReturnedValue();
}
ReturnedValue Document::method_documentElement(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -1637,19 +1629,19 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
{
V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *, int)
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
{
Scope scope(f->engine());
const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f);
QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine);
- Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r));
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
- w->setPrototype(proto);
+ w->setPrototypeUnchecked(proto);
return w.asReturnedValue();
}
- static ReturnedValue call(const FunctionObject *, const Value *, const Value *, int) {
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *, const Value *, int) {
return Encode::undefined();
}
@@ -2049,7 +2041,7 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
{
Scope scope(v4);
- Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocObject<QQmlXMLHttpRequestCtor>(v4));
+ Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocate<QQmlXMLHttpRequestCtor>(v4));
ScopedString s(scope, v4->newString(QStringLiteral("XMLHttpRequest")));
v4->globalObject->defineReadonlyProperty(s, ctor);
diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h
index f2836d8301..7515ef8dcc 100644
--- a/src/qml/qml/qqmlxmlhttprequest_p.h
+++ b/src/qml/qml/qqmlxmlhttprequest_p.h
@@ -55,7 +55,7 @@
#include <QtCore/qglobal.h>
#include <private/qqmlglobal_p.h>
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
+QT_REQUIRE_CONFIG(qml_xml_http_request);
QT_BEGIN_NAMESPACE
@@ -64,7 +64,5 @@ void qt_rem_qmlxmlhttprequest(QV4::ExecutionEngine *engine, void *);
QT_END_NAMESPACE
-#endif // xmlstreamreader && qml_network
-
#endif // QQMLXMLHTTPREQUEST_P_H
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index f04bee7f19..e6f68a30b3 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -44,7 +44,9 @@
#include <private/qqmlcomponent_p.h>
#include <private/qqmlloggingcategory_p.h>
#include <private/qqmlstringconverters_p.h>
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <private/qv8engine_p.h>
#include <private/qqmldelayedcallqueue_p.h>
#include <QFileInfo>
@@ -65,6 +67,7 @@
#include <private/qv4jsonobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4stackframe_p.h>
#include <QtCore/qstring.h>
#include <QtCore/qdatetime.h>
@@ -138,7 +141,9 @@ void Heap::QtObject::init(QQmlEngine *qmlEngine)
o->defineDefaultProperty(QStringLiteral("btoa"), QV4::QtObject::method_btoa);
o->defineDefaultProperty(QStringLiteral("atob"), QV4::QtObject::method_atob);
o->defineDefaultProperty(QStringLiteral("resolvedUrl"), QV4::QtObject::method_resolvedUrl);
+#if QT_CONFIG(qml_locale)
o->defineDefaultProperty(QStringLiteral("locale"), QV4::QtObject::method_locale);
+#endif
o->defineDefaultProperty(QStringLiteral("binding"), QV4::QtObject::method_binding);
if (qmlEngine) {
@@ -193,35 +198,35 @@ ReturnedValue QtObject::findAndAdd(const QString *name, bool &foundProperty) con
return Encode::undefined();
}
-ReturnedValue QtObject::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QtObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
bool hasProp = false;
if (hasProperty == nullptr) {
hasProperty = &hasProp;
}
- ReturnedValue ret = QV4::Object::get(m, name, hasProperty);
+ ReturnedValue ret = QV4::Object::virtualGet(m, id, receiver, hasProperty);
if (*hasProperty) {
return ret;
}
auto that = static_cast<const QtObject*>(m);
if (!that->d()->isComplete()) {
- const QString key = name->toQString();
+ const QString key = id.toQString();
ret = that->findAndAdd(&key, *hasProperty);
}
return ret;
}
-void QtObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+void QtObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{
auto that = static_cast<QtObject*>(m);
if (!that->d()->isComplete()) {
that->addAll();
}
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+ QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
/*!
@@ -1106,7 +1111,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va
qmlerror->put((s = v4->newString(QStringLiteral("columnNumber"))), (v = QV4::Primitive::fromInt32(error.column())));
qmlerror->put((s = v4->newString(QStringLiteral("fileName"))), (v = v4->newString(error.url().toString())));
qmlerror->put((s = v4->newString(QStringLiteral("message"))), (v = v4->newString(error.description())));
- qmlerrors->putIndexed(ii, qmlerror);
+ qmlerrors->put(ii, qmlerror);
}
v = v4->newString(errorstr);
@@ -1152,7 +1157,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va
if (!parentArg)
THROW_GENERIC_ERROR("Qt.createQmlObject(): Missing parent object");
- QQmlTypeData *typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType(
+ QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType(
qml.toUtf8(), url, QQmlTypeLoader::Synchronous);
Q_ASSERT(typeData->isCompleteOrError());
QQmlComponent component(engine);
@@ -1309,6 +1314,7 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va
return QV4::QObjectWrapper::wrap(scope.engine, c);
}
+#if QT_CONFIG(qml_locale)
/*!
\qmlmethod Qt::locale(name)
@@ -1343,6 +1349,7 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co
return QQmlLocale::locale(scope.engine, code);
}
+#endif
void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction)
{
@@ -1416,7 +1423,7 @@ ReturnedValue QtObject::method_binding(const FunctionObject *b, const Value *, c
if (!f)
THROW_TYPE_ERROR_WITH_MESSAGE("binding(): argument (binding expression) must be a function");
- return Encode(scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f));
+ return Encode(scope.engine->memoryManager->allocate<QQmlBindingFunction>(f));
}
@@ -1551,7 +1558,7 @@ static ReturnedValue writeToConsole(const FunctionObject *b, const Value *, cons
if (i != start)
result.append(QLatin1Char(' '));
- if (argv[i].as<ArrayObject>())
+ if (argv[i].isManaged() && argv[i].managed()->isArrayLike())
result += QLatin1Char('[') + argv[i].toQStringNoThrow() + QLatin1Char(']');
else
result.append(argv[i].toQStringNoThrow());
@@ -1795,7 +1802,7 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext
globalObject->defineDefaultProperty(QStringLiteral("print"), QV4::ConsoleObject::method_log);
- QV4::ScopedObject console(scope, globalObject->engine()->memoryManager->allocObject<QV4::ConsoleObject>());
+ QV4::ScopedObject console(scope, globalObject->engine()->memoryManager->allocate<QV4::ConsoleObject>());
globalObject->defineDefaultProperty(QStringLiteral("console"), console);
}
@@ -1850,6 +1857,10 @@ ReturnedValue GlobalExtensions::method_qsTranslate(const FunctionObject *b, cons
if (argc > i)
n = argv[i].toInt32();
+ if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr))
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureTranslation();
+
QString result = QCoreApplication::translate(context.toUtf8().constData(),
text.toUtf8().constData(),
comment.toUtf8().constData(),
@@ -1953,6 +1964,10 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value
if (argc > 2)
n = argv[2].toInt32();
+ if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr))
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureTranslation();
+
QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(),
comment.toUtf8().constData(), n);
@@ -2033,6 +2048,10 @@ ReturnedValue GlobalExtensions::method_qsTrId(const FunctionObject *b, const Val
if (argc > 1)
n = argv[1].toInt32();
+ if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr))
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureTranslation();
+
return Encode(scope.engine->newString(qtTrId(argv[0].toQStringNoThrow().toUtf8().constData(), n)));
}
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index ee3b5f7d6e..4b3814c8b8 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -93,8 +93,8 @@ struct QtObject : Object
{
V4_OBJECT2(QtObject, Object)
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static ReturnedValue method_isQtObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_rgba(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
@@ -126,7 +126,9 @@ struct QtObject : Object
static ReturnedValue method_resolvedUrl(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_createQmlObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_createComponent(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+#if QT_CONFIG(qml_locale)
static ReturnedValue method_locale(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+#endif
static ReturnedValue method_binding(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_platform(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp
index 038a75d50a..78c9fe822b 100644
--- a/src/qml/qml/v8/qv8engine.cpp
+++ b/src/qml/qml/v8/qv8engine.cpp
@@ -39,14 +39,21 @@
#include "qv8engine_p.h"
+#if QT_CONFIG(qml_sequence_object)
#include "qv4sequenceobject_p.h"
+#endif
+
#include "private/qjsengine_p.h"
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmllist_p.h>
#include <private/qqmlengine_p.h>
+#if QT_CONFIG(qml_xml_http_request)
#include <private/qqmlxmlhttprequest_p.h>
+#endif
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <private/qqmlglobal_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmlplatform_p.h>
@@ -78,6 +85,7 @@
#include <private/qv4script_p.h>
#include <private/qv4include_p.h>
#include <private/qv4jsonobject_p.h>
+#include <private/qv4identifiertable_p.h>
Q_DECLARE_METATYPE(QList<int>)
@@ -123,11 +131,12 @@ static void restoreJSValue(QDataStream &stream, void *data)
}
}
-QV8Engine::QV8Engine(QJSEngine *qq, QV4::ExecutionEngine *v4)
- : q(qq)
- , m_engine(nullptr)
+QV8Engine::QV8Engine(QV4::ExecutionEngine *v4)
+ : m_engine(nullptr)
, m_v4Engine(v4)
+#if QT_CONFIG(qml_xml_http_request)
, m_xmlHttpRequestData(nullptr)
+#endif
{
#ifdef Q_PROCESSOR_X86_32
if (!qCpuHasFeature(SSE2)) {
@@ -157,7 +166,7 @@ QV8Engine::~QV8Engine()
qDeleteAll(m_extensionData);
m_extensionData.clear();
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
+#if QT_CONFIG(qml_xml_http_request)
qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData);
m_xmlHttpRequestData = nullptr;
#endif
@@ -180,14 +189,16 @@ void QV8Engine::initializeGlobal()
QV4::Scope scope(m_v4Engine);
QV4::GlobalExtensions::init(m_v4Engine->globalObject, QJSEngine::AllExtensions);
- QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocObject<QV4::QtObject>(m_engine));
+ QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocate<QV4::QtObject>(m_engine));
m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt);
+#if QT_CONFIG(qml_locale)
QQmlLocale::registerStringLocaleCompare(m_v4Engine);
QQmlDateExtension::registerExtension(m_v4Engine);
QQmlNumberExtension::registerExtension(m_v4Engine);
+#endif
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
+#if QT_CONFIG(qml_xml_http_request)
qt_add_domexceptions(m_v4Engine);
m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine);
#endif
@@ -196,8 +207,10 @@ void QV8Engine::initializeGlobal()
{
for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) {
- if (m_v4Engine->globalObject->internalClass()->nameMap.at(i))
- m_illegalNames.insert(m_v4Engine->globalObject->internalClass()->nameMap.at(i)->string);
+ if (m_v4Engine->globalObject->internalClass()->nameMap.at(i).isString()) {
+ QV4::PropertyKey id = m_v4Engine->globalObject->internalClass()->nameMap.at(i);
+ m_illegalNames.insert(id.toQString());
+ }
}
}
}
@@ -210,25 +223,25 @@ static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
QV4::Scope scope(v4);
bool instanceOfObject = false;
- QV4::ScopedObject p(scope, object->prototype());
+ QV4::ScopedObject p(scope, object->getPrototypeOf());
while (p) {
if (p->d() == v4->objectPrototype()->d()) {
instanceOfObject = true;
break;
}
- p = p->prototype();
+ p = p->getPrototypeOf();
}
if (!instanceOfObject)
return;
- QV4::InternalClass *frozen = object->internalClass()->propertiesFrozen();
+ QV4::Heap::InternalClass *frozen = object->internalClass()->propertiesFrozen();
if (object->internalClass() == frozen)
return;
object->setInternalClass(frozen);
QV4::ScopedObject o(scope);
for (uint i = 0; i < frozen->size; ++i) {
- if (!frozen->nameMap.at(i))
+ if (!frozen->nameMap.at(i).isStringOrSymbol())
continue;
o = *object->propertyData(i);
if (o)
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
index b6a378667e..23559618ef 100644
--- a/src/qml/qml/v8/qv8engine_p.h
+++ b/src/qml/qml/v8/qv8engine_p.h
@@ -67,6 +67,7 @@
#include <private/qv4value_p.h>
#include <private/qv4identifier_p.h>
#include <private/qv4context_p.h>
+#include <private/qv4stackframe_p.h>
#include <private/qqmldelayedcallqueue_p.h>
QT_BEGIN_NAMESPACE
@@ -154,10 +155,9 @@ class Q_QML_PRIVATE_EXPORT QV8Engine
friend class QJSEngine;
public:
// static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; }
- static QV4::ExecutionEngine *getV4(QJSEngine *q) { return q->handle(); }
static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; }
- QV8Engine(QJSEngine* qq, QV4::ExecutionEngine *v4);
+ QV8Engine(QV4::ExecutionEngine *v4);
virtual ~QV8Engine();
// This enum should be in sync with QQmlEngine::ObjectOwnership
@@ -170,10 +170,11 @@ public:
void initQmlGlobalObject();
void setEngine(QQmlEngine *engine);
QQmlEngine *engine() { return m_engine; }
- QJSEngine *publicEngine() { return q; }
QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; }
+#if QT_CONFIG(qml_xml_http_request)
void *xmlHttpRequestData() const { return m_xmlHttpRequestData; }
+#endif
void freezeObject(const QV4::Value &value);
@@ -202,13 +203,14 @@ public:
int consoleCountHelper(const QString &file, quint16 line, quint16 column);
protected:
- QJSEngine* q;
QQmlEngine *m_engine;
QQmlDelayedCallQueue m_delayedCallQueue;
QV4::ExecutionEngine *m_v4Engine;
+#if QT_CONFIG(qml_xml_http_request)
void *m_xmlHttpRequestData;
+#endif
QVector<Deletable *> m_extensionData;
diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h
index 6e92867cf5..e02dfa5ed4 100644
--- a/src/qml/qtqmlglobal.h
+++ b/src/qml/qtqmlglobal.h
@@ -52,6 +52,7 @@
# endif
#else
# define QT_FEATURE_qml_debug -1
+# define QT_FEATURE_qml_sequence_object 1
#endif
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 4c0ba338d8..bb880451a9 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -57,6 +57,8 @@
# include <QtQml/private/qtqml-config_p.h>
#endif
+#define Q_QML_PRIVATE_API_VERSION 2
+
#define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT
#if !defined(QT_QMLDEVTOOLS_LIB) && !defined(QT_BUILD_QMLDEVTOOLS_LIB)
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index 7a12813f0c..7aba0689d1 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -93,10 +93,10 @@ struct DelegateModelGroupFunction : QV4::FunctionObject
static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg))
{
- return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code);
+ return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code);
}
- static ReturnedValue call(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc)
+ static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc)
{
QV4::Scope scope(that->engine());
QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that));
@@ -200,8 +200,7 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent)
*/
QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
- : m_delegate(nullptr)
- , m_cacheMetaType(nullptr)
+ : m_cacheMetaType(nullptr)
, m_context(ctxt)
, m_parts(nullptr)
, m_filterGroup(QStringLiteral("items"))
@@ -214,6 +213,7 @@ QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
, m_transaction(false)
, m_incubatorCleanupScheduled(false)
, m_waitingToFetchMore(false)
+ , m_useFirstColumnOnly(true)
, m_cacheItems(nullptr)
, m_items(nullptr)
, m_persistedItems(nullptr)
@@ -228,6 +228,11 @@ QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate()
m_cacheMetaType->release();
}
+int QQmlDelegateModelPrivate::adaptorModelCount() const
+{
+ return m_useFirstColumnOnly ? m_adaptorModel.rowCount() : m_adaptorModel.count();
+}
+
void QQmlDelegateModelPrivate::requestMoreIfNecessary()
{
Q_Q(QQmlDelegateModel);
@@ -263,6 +268,8 @@ QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent)
QQmlDelegateModel::~QQmlDelegateModel()
{
Q_D(QQmlDelegateModel);
+ d->disconnectFromAbstractItemModel();
+ d->m_adaptorModel.setObject(nullptr, this);
for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) {
if (cacheItem->object) {
@@ -336,7 +343,7 @@ void QQmlDelegateModel::componentComplete()
static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup();
QVector<Compositor::Insert> inserts;
- d->m_count = d->m_adaptorModel.count();
+ d->m_count = d->adaptorModelCount();
d->m_compositor.append(
&d->m_adaptorModel,
0,
@@ -368,6 +375,54 @@ QVariant QQmlDelegateModel::model() const
return d->m_adaptorModel.model();
}
+void QQmlDelegateModelPrivate::connectToAbstractItemModel()
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_adaptorModel.adaptsAim())
+ return;
+
+ auto aim = m_adaptorModel.aim();
+
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()),
+ q, QQmlDelegateModel, SLOT(_q_modelReset()));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
+ q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+}
+
+void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_adaptorModel.adaptsAim())
+ return;
+
+ auto aim = m_adaptorModel.aim();
+
+ QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ q, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ q, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ QObject::disconnect(aim, SIGNAL(modelReset()),
+ q, SLOT(_q_modelReset()));
+ QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
+ q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+}
+
void QQmlDelegateModel::setModel(const QVariant &model)
{
Q_D(QQmlDelegateModel);
@@ -375,7 +430,10 @@ void QQmlDelegateModel::setModel(const QVariant &model)
if (d->m_complete)
_q_itemsRemoved(0, d->m_count);
+ d->disconnectFromAbstractItemModel();
d->m_adaptorModel.setModel(model, this, d->m_context->engine());
+ d->connectToAbstractItemModel();
+
d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles);
for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) {
d->m_adaptorModel.replaceWatchedRoles(
@@ -383,7 +441,7 @@ void QQmlDelegateModel::setModel(const QVariant &model)
}
if (d->m_complete) {
- _q_itemsInserted(0, d->m_adaptorModel.count());
+ _q_itemsInserted(0, d->adaptorModelCount());
d->requestMoreIfNecessary();
}
}
@@ -409,7 +467,7 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
return;
}
bool wasValid = d->m_delegate != nullptr;
- d->m_delegate = delegate;
+ d->m_delegate.setObject(delegate, this);
d->m_delegateValidated = false;
if (wasValid && d->m_complete) {
for (int i = 1; i < d->m_groupCount; ++i) {
@@ -470,12 +528,16 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root)
if (changed || !d->m_adaptorModel.isValid()) {
const int oldCount = d->m_count;
d->m_adaptorModel.rootIndex = modelIndex;
- if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model.
+ if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) {
+ // The previous root index was invalidated, so we need to reconnect the model.
+ d->disconnectFromAbstractItemModel();
d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine());
+ d->connectToAbstractItemModel();
+ }
if (d->m_adaptorModel.canFetchMore())
d->m_adaptorModel.fetchMore();
if (d->m_complete) {
- const int newCount = d->m_adaptorModel.count();
+ const int newCount = d->adaptorModelCount();
if (oldCount)
_q_itemsRemoved(0, oldCount);
if (newCount)
@@ -487,6 +549,35 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root)
}
/*!
+ \qmlproperty int QtQml.Models::DelegateModel::rows
+
+ Contains the number of rows in the model. If the model
+ is a list of items, it will be equal to the number of items
+ in the list.
+
+ \since QtQml.Models 2.12
+*/
+int QQmlDelegateModel::rows() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.rowCount();
+}
+
+/*!
+ \qmlproperty int QtQml.Models::DelegateModel::columns
+
+ Contains the number of columns in the model. If the model
+ is a list of items, it will be equal to \c 1.
+
+ \since QtQml.Models 2.12
+*/
+int QQmlDelegateModel::columns() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.columnCount();
+}
+
+/*!
\qmlmethod QModelIndex QtQml.Models::DelegateModel::modelIndex(int index)
QAbstractItemModel provides a hierarchical tree of data, whereas
@@ -536,25 +627,24 @@ int QQmlDelegateModel::count() const
QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object)
{
- QQmlDelegateModel::ReleaseFlags stat = nullptr;
if (!object)
- return stat;
-
- if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) {
- if (cacheItem->releaseObject()) {
- cacheItem->destroyObject();
- emitDestroyingItem(object);
- if (cacheItem->incubationTask) {
- releaseIncubator(cacheItem->incubationTask);
- cacheItem->incubationTask = nullptr;
- }
- cacheItem->Dispose();
- stat |= QQmlInstanceModel::Destroyed;
- } else {
- stat |= QQmlDelegateModel::Referenced;
- }
+ return QQmlDelegateModel::ReleaseFlags(0);
+
+ QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object);
+ if (!cacheItem)
+ return QQmlDelegateModel::ReleaseFlags(0);
+
+ if (!cacheItem->releaseObject())
+ return QQmlDelegateModel::Referenced;
+
+ cacheItem->destroyObject();
+ emitDestroyingItem(object);
+ if (cacheItem->incubationTask) {
+ releaseIncubator(cacheItem->incubationTask);
+ cacheItem->incubationTask = nullptr;
}
- return stat;
+ cacheItem->Dispose();
+ return QQmlInstanceModel::Destroyed;
}
/*
@@ -808,6 +898,12 @@ QObject *QQmlDelegateModel::parts()
return d->m_parts;
}
+const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr;
+}
+
void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
{
for (int i = 1; i < m_groupCount; ++i)
@@ -862,6 +958,13 @@ void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTa
}
}
+void QQmlDelegateModelPrivate::addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it)
+{
+ m_cache.insert(it.cacheIndex, item);
+ m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+}
+
void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem)
{
int cidx = m_cache.lastIndexOf(cacheItem);
@@ -951,10 +1054,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
return nullptr;
cacheItem->groups = it->flags;
-
- m_cache.insert(it.cacheIndex, cacheItem);
- m_compositor.setFlags(it, 1, Compositor::CacheFlag);
- Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ addCacheItem(cacheItem, it);
}
// Bump the reference counts temporarily so neither the content data or the delegate object
@@ -1046,7 +1146,10 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index)
if (!it->inCache())
return QQmlIncubator::Null;
- return d->m_cache.at(it.cacheIndex)->incubationTask->status();
+ if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask)
+ return incubationTask->status();
+
+ return QQmlIncubator::Ready;
}
QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name)
@@ -1266,8 +1369,12 @@ void QQmlDelegateModel::_q_itemsInserted(int index, int count)
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
for (int i = 0, c = cache.count(); i < c; ++i) {
QQmlDelegateModelItem *item = cache.at(i);
- if (item->modelIndex() >= index)
- item->setModelIndex(item->modelIndex() + count);
+ if (item->modelIndex() >= index) {
+ const int newIndex = item->modelIndex() + count;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ }
}
QVector<Compositor::Insert> inserts;
@@ -1408,10 +1515,14 @@ void QQmlDelegateModel::_q_itemsRemoved(int index, int count)
if (!d->m_cache.contains(item))
continue;
- if (item->modelIndex() >= index + count)
- item->setModelIndex(item->modelIndex() - count);
- else if (item->modelIndex() >= index)
- item->setModelIndex(-1);
+ if (item->modelIndex() >= index + count) {
+ const int newIndex = item->modelIndex() - count;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ } else if (item->modelIndex() >= index) {
+ item->setModelIndex(-1, -1, -1);
+ }
}
QVector<Compositor::Remove> removes;
@@ -1456,10 +1567,17 @@ void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count)
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
for (int i = 0, c = cache.count(); i < c; ++i) {
QQmlDelegateModelItem *item = cache.at(i);
- if (item->modelIndex() >= from && item->modelIndex() < from + count)
- item->setModelIndex(item->modelIndex() - from + to);
- else if (item->modelIndex() >= minimum && item->modelIndex() < maximum)
- item->setModelIndex(item->modelIndex() + difference);
+ if (item->modelIndex() >= from && item->modelIndex() < from + count) {
+ const int newIndex = item->modelIndex() - from + to;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) {
+ const int newIndex = item->modelIndex() + difference;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ }
}
QVector<Compositor::Remove> removes;
@@ -1510,13 +1628,13 @@ void QQmlDelegateModel::_q_modelReset()
d->m_adaptorModel.rootIndex = QModelIndex();
if (d->m_complete) {
- d->m_count = d->m_adaptorModel.count();
+ d->m_count = d->adaptorModelCount();
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
for (int i = 0, c = cache.count(); i < c; ++i) {
QQmlDelegateModelItem *item = cache.at(i);
if (item->modelIndex() != -1)
- item->setModelIndex(-1);
+ item->setModelIndex(-1, -1, -1);
}
QVector<Compositor::Remove> removes;
@@ -1552,7 +1670,8 @@ void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int b
if (index.parent() == parent && index.row() >= begin && index.row() <= end) {
const int oldCount = d->m_count;
d->m_count = 0;
- d->m_adaptorModel.invalidateModel(this);
+ d->disconnectFromAbstractItemModel();
+ d->m_adaptorModel.invalidateModel();
if (d->m_complete && oldCount > 0) {
QVector<Compositor::Remove> removes;
@@ -1588,8 +1707,15 @@ void QQmlDelegateModel::_q_rowsMoved(
void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles)
{
Q_D(QQmlDelegateModel);
- if (begin.parent() == d->m_adaptorModel.rootIndex)
- _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles);
+ if (begin.parent() != d->m_adaptorModel.rootIndex)
+ return;
+
+ int rowCount = end.row() - begin.row() + 1;
+
+ for (int col = begin.column(); col <= end.column(); ++col) {
+ int startIndex = d->m_adaptorModel.indexAt(begin.row(), col);
+ _q_itemsChanged(startIndex, rowCount, roles);
+ }
}
bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const QList< QPersistentModelIndex >& parents) const
@@ -1804,7 +1930,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const
QV4::ScopedValue v(scope);
uint arrayLength = array->getLength();
for (uint i = 0; i < arrayLength; ++i) {
- v = array->getIndexed(i);
+ v = array->get(i);
const QString groupName = v->toQString();
int index = groupNames.indexOf(groupName);
if (index != -1)
@@ -1906,8 +2032,7 @@ void QV4::Heap::QQmlDelegateModelItemObject::destroy()
}
-QQmlDelegateModelItem::QQmlDelegateModelItem(
- QQmlDelegateModelItemMetaType *metaType, int modelIndex)
+QQmlDelegateModelItem::QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column)
: v4(metaType->v4Engine)
, metaType(metaType)
, contextData(nullptr)
@@ -1918,6 +2043,8 @@ QQmlDelegateModelItem::QQmlDelegateModelItem(
, scriptRef(0)
, groups(0)
, index(modelIndex)
+ , row(row)
+ , column(column)
{
metaType->addref();
}
@@ -1952,6 +2079,26 @@ void QQmlDelegateModelItem::Dispose()
delete this;
}
+void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn)
+{
+ if (idx == index)
+ return;
+
+ const int prevRow = row;
+ const int prevColumn = column;
+
+ index = idx;
+ row = newRow;
+ column = newColumn;
+
+ Q_EMIT modelIndexChanged();
+
+ if (row != prevRow)
+ emit rowChanged();
+ if (column != prevColumn)
+ emit columnChanged();
+}
+
void QQmlDelegateModelItem::destroyObject()
{
Q_ASSERT(object);
@@ -2080,22 +2227,29 @@ QQmlDelegateModelAttached::QQmlDelegateModelAttached(
, m_previousGroups(cacheItem->groups)
{
QQml_setParent_noEvent(this, parent);
+ resetCurrentIndex();
+ // Let m_previousIndex be equal to m_currentIndex
+ std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex));
+
+ if (!cacheItem->metaType->metaObject)
+ cacheItem->metaType->initializeMetaObject();
+
+ QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject;
+ cacheItem->metaType->metaObject->addref();
+}
+
+void QQmlDelegateModelAttached::resetCurrentIndex()
+{
if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) {
for (int i = 1; i < qMin<int>(m_cacheItem->metaType->groupCount, Compositor::MaximumGroupCount); ++i)
- m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i];
+ m_currentIndex[i] = incubationTask->index[i];
} else {
QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model);
Compositor::iterator it = model->m_compositor.find(
Compositor::Cache, model->m_cache.indexOf(m_cacheItem));
for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i)
- m_currentIndex[i] = m_previousIndex[i] = it.index[i];
+ m_currentIndex[i] = it.index[i];
}
-
- if (!cacheItem->metaType->metaObject)
- cacheItem->metaType->initializeMetaObject();
-
- QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject;
- cacheItem->metaType->metaObject->addref();
}
/*!
@@ -2483,9 +2637,9 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
model->m_cacheMetaType->initializePrototype();
QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine;
QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(cacheItem));
+ QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem));
QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value());
- o->setPrototype(p);
+ o->setPrototypeOf(p);
++cacheItem->scriptRef;
return QQmlV4Handle(o);
@@ -3194,7 +3348,10 @@ QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index)
if (!it->inCache())
return QQmlIncubator::Null;
- return model->m_cache.at(it.cacheIndex)->incubationTask->status();
+ if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask)
+ return incubationTask->status();
+
+ return QQmlIncubator::Ready;
}
int QQmlPartsModel::indexOf(QObject *item, QObject *) const
@@ -3214,7 +3371,10 @@ void QQmlPartsModel::createdPackage(int index, QQuickPackage *package)
void QQmlPartsModel::initPackage(int index, QQuickPackage *package)
{
- emit initItem(index, package->part(m_part));
+ if (m_modelUpdatePending)
+ m_pendingPackageInitializations << index;
+ else
+ emit initItem(index, package->part(m_part));
}
void QQmlPartsModel::destroyingPackage(QQuickPackage *package)
@@ -3226,9 +3386,22 @@ void QQmlPartsModel::destroyingPackage(QQuickPackage *package)
void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset)
{
+ m_modelUpdatePending = false;
emit modelUpdated(changeSet, reset);
if (changeSet.difference() != 0)
emit countChanged();
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+ QVector<int> pendingPackageInitializations;
+ qSwap(pendingPackageInitializations, m_pendingPackageInitializations);
+ for (int index : pendingPackageInitializations) {
+ if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup))
+ continue;
+ QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous);
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
+ emit initItem(index, package->part(m_part));
+ model->release(object);
+ }
}
//============================================================================
@@ -3238,7 +3411,7 @@ struct QQmlDelegateModelGroupChange : QV4::Object
V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object)
static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) {
- return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>();
+ return e->memoryManager->allocate<QQmlDelegateModelGroupChange>();
}
static QV4::ReturnedValue method_get_index(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) {
@@ -3275,49 +3448,49 @@ struct QQmlDelegateModelGroupChangeArray : public QV4::Object
public:
static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes)
{
- return engine->memoryManager->allocObject<QQmlDelegateModelGroupChangeArray>(changes);
+ return engine->memoryManager->allocate<QQmlDelegateModelGroupChangeArray>(changes);
}
quint32 count() const { return d()->changes->count(); }
const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); }
- static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
{
- Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>());
- QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine();
- QV4::Scope scope(v4);
- QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m));
-
- if (index >= array->count()) {
- if (hasProperty)
- *hasProperty = false;
- return QV4::Primitive::undefinedValue().asReturnedValue();
- }
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>());
+ QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine();
+ QV4::Scope scope(v4);
+ QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m));
+
+ if (index >= array->count()) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Primitive::undefinedValue().asReturnedValue();
+ }
- const QQmlChangeSet::Change &change = array->at(index);
+ const QQmlChangeSet::Change &change = array->at(index);
- QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value());
- QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4));
- object->setPrototype(changeProto);
- object->d()->change = change;
+ QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value());
+ QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4));
+ object->setPrototypeOf(changeProto);
+ object->d()->change = change;
- if (hasProperty)
- *hasProperty = true;
- return object.asReturnedValue();
- }
+ if (hasProperty)
+ *hasProperty = true;
+ return object.asReturnedValue();
+ }
- static QV4::ReturnedValue get(const QV4::Managed *m, QV4::String *name, bool *hasProperty)
- {
Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>());
const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m);
- if (name->equals(array->engine()->id_length())) {
+ if (id == array->engine()->id_length()->propertyKey()) {
if (hasProperty)
*hasProperty = true;
return QV4::Encode(array->count());
}
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
};
diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h
index b0786cd088..65b1b82d70 100644
--- a/src/qml/types/qqmldelegatemodel_p.h
+++ b/src/qml/types/qqmldelegatemodel_p.h
@@ -88,6 +88,8 @@ class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public
Q_PROPERTY(QQmlListProperty<QQmlDelegateModelGroup> groups READ groups CONSTANT)
Q_PROPERTY(QObject *parts READ parts CONSTANT)
Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged)
+ Q_PROPERTY(int rows READ rows NOTIFY rowsChanged REVISION 12)
+ Q_PROPERTY(int columns READ columns NOTIFY columnsChanged REVISION 12)
Q_CLASSINFO("DefaultProperty", "delegate")
Q_INTERFACES(QQmlParserStatus)
public:
@@ -107,6 +109,9 @@ public:
QVariant rootIndex() const;
void setRootIndex(const QVariant &root);
+ int rows() const;
+ int columns() const;
+
Q_INVOKABLE QVariant modelIndex(int idx) const;
Q_INVOKABLE QVariant parentModelIndex() const;
@@ -130,6 +135,8 @@ public:
QQmlListProperty<QQmlDelegateModelGroup> groups();
QObject *parts();
+ const QAbstractItemModel *abstractItemModel() const override;
+
bool event(QEvent *) override;
static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj);
@@ -138,6 +145,8 @@ Q_SIGNALS:
void filterGroupChanged();
void defaultGroupsChanged();
void rootIndexChanged();
+ Q_REVISION(12) void rowsChanged();
+ Q_REVISION(12) void columnsChanged();
private Q_SLOTS:
void _q_itemsChanged(int index, int count, const QVector<int> &roles);
@@ -212,6 +221,7 @@ public:
QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent);
~QQmlDelegateModelAttached() {}
+ void resetCurrentIndex();
void setCacheItem(QQmlDelegateModelItem *item);
QQmlDelegateModel *model() const;
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
index 18980cfd7c..c4e253800a 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -68,7 +68,7 @@ typedef QQmlListCompositor Compositor;
class QQmlDelegateModelAttachedMetaObject;
-class QQmlDelegateModelItemMetaType : public QQmlRefCount
+class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount
{
public:
QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames);
@@ -95,9 +95,11 @@ class QQmlDelegateModelItem : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged)
+ Q_PROPERTY(int row MEMBER row NOTIFY rowChanged)
+ Q_PROPERTY(int column MEMBER column NOTIFY columnChanged)
Q_PROPERTY(QObject *model READ modelObject CONSTANT)
public:
- QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex);
+ QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column);
~QQmlDelegateModelItem();
void referenceObject() { ++objectRef; }
@@ -121,7 +123,7 @@ public:
int groupIndex(Compositor::Group group);
int modelIndex() const { return index; }
- void setModelIndex(int idx) { index = idx; Q_EMIT modelIndexChanged(); }
+ virtual void setModelIndex(int idx, int newRow, int newColumn);
virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); }
@@ -148,9 +150,13 @@ public:
Q_SIGNALS:
void modelIndexChanged();
+ void rowChanged();
+ void columnChanged();
protected:
void objectDestroyed(QObject *);
+ int row;
+ int column;
};
namespace QV4 {
@@ -190,8 +196,8 @@ public:
void statusChanged(Status) override;
void setInitialState(QObject *) override;
- QQmlDelegateModelItem *incubating;
- QQmlDelegateModelPrivate *vdm;
+ QQmlDelegateModelItem *incubating = nullptr;
+ QQmlDelegateModelPrivate *vdm = nullptr;
int index[QQmlListCompositor::MaximumGroupCount];
};
@@ -256,6 +262,8 @@ public:
void init();
void connectModel(QQmlAdaptorModel *model);
+ void connectToAbstractItemModel();
+ void disconnectFromAbstractItemModel();
void requestMoreIfNecessary();
QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode);
@@ -269,6 +277,7 @@ public:
Q_EMIT q_func()->initItem(incubationTask->index[m_compositorGroup], item); }
void emitDestroyingPackage(QQuickPackage *package);
void emitDestroyingItem(QObject *item) { Q_EMIT q_func()->destroyingItem(item); }
+ void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it);
void removeCacheItem(QQmlDelegateModelItem *cacheItem);
void updateFilterGroup();
@@ -295,6 +304,8 @@ public:
bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups);
+ int adaptorModelCount() const;
+
static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group);
static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property);
static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index);
@@ -305,7 +316,7 @@ public:
QQmlAdaptorModel m_adaptorModel;
QQmlListCompositor m_compositor;
- QQmlComponent *m_delegate;
+ QQmlStrongJSQObjectReference<QQmlComponent> m_delegate;
QQmlDelegateModelItemMetaType *m_cacheMetaType;
QPointer<QQmlContext> m_context;
QQmlDelegateModelParts *m_parts;
@@ -327,6 +338,7 @@ public:
bool m_transaction : 1;
bool m_incubatorCleanupScheduled : 1;
bool m_waitingToFetchMore : 1;
+ bool m_useFirstColumnOnly : 1;
union {
struct {
@@ -378,8 +390,10 @@ private:
QString m_part;
QString m_filterGroup;
QList<QByteArray> m_watchedRoles;
+ QVector<int> m_pendingPackageInitializations; // vector holds model indices
Compositor::Group m_compositorGroup;
bool m_inheritGroup;
+ bool m_modelUpdatePending = true;
};
class QMetaPropertyBuilder;
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index 830c2bef5a..2325eca469 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -62,6 +62,8 @@
#include <QtCore/qdatetime.h>
#include <QScopedValueRollback>
+Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*);
+
QT_BEGIN_NAMESPACE
// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
@@ -124,8 +126,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data
const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
{
- const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
- const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
+ const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
+ const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
Role *r = new Role;
r->name = key;
@@ -227,6 +229,10 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV
data.value<QJSValue>().isCallable()) {
type = Role::Function;
break;
+ } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>()
+ && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) {
+ type = Role::String;
+ break;
} else {
type = Role::List;
break;
@@ -261,6 +267,75 @@ const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const
return r;
}
+StringOrTranslation::StringOrTranslation(const QString &s)
+{
+ d.setFlag();
+ setString(s);
+}
+
+StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding)
+{
+ d.setFlag();
+ clear();
+ d = binding;
+}
+
+StringOrTranslation::~StringOrTranslation()
+{
+ clear();
+}
+
+void StringOrTranslation::setString(const QString &s)
+{
+ d.setFlag();
+ clear();
+ QStringData *stringData = const_cast<QString &>(s).data_ptr();
+ d = stringData;
+ if (stringData)
+ stringData->ref.ref();
+}
+
+void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding)
+{
+ d.setFlag();
+ clear();
+ d = binding;
+}
+
+QString StringOrTranslation::toString(const QQmlListModel *owner) const
+{
+ if (d.isNull())
+ return QString();
+ if (d.isT1()) {
+ QStringDataPtr holder = { d.asT1() };
+ holder.ptr->ref.ref();
+ return QString(holder);
+ }
+ if (!owner)
+ return QString();
+ return d.asT2()->valueAsString(owner->m_compilationUnit->data);
+}
+
+QString StringOrTranslation::asString() const
+{
+ if (d.isNull())
+ return QString();
+ if (!d.isT1())
+ return QString();
+ QStringDataPtr holder = { d.asT1() };
+ holder.ptr->ref.ref();
+ return QString(holder);
+}
+
+void StringOrTranslation::clear()
+{
+ if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) {
+ if (!strData->ref.deref())
+ QStringData::deallocate(strData);
+ }
+ d = static_cast<QStringData *>(nullptr);
+}
+
QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex)
{
ListElement *e = elements[elementIndex];
@@ -512,7 +587,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
- o = a->getIndexed(j);
+ o = a->get(j);
subModel->append(o);
}
@@ -593,7 +668,7 @@ void ListModel::set(int elementIndex, QV4::Object *object)
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
- o = a->getIndexed(j);
+ o = a->get(j);
subModel->append(o);
}
@@ -717,11 +792,11 @@ ModelNodeMetaObject *ListElement::objectCache()
return ModelNodeMetaObject::get(m_objectCache);
}
-QString *ListElement::getStringProperty(const ListLayout::Role &role)
+StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role)
{
char *mem = getPropertyMemory(role);
- QString *s = reinterpret_cast<QString *>(mem);
- return s->data_ptr() ? s : nullptr;
+ StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
+ return s;
}
QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
@@ -806,9 +881,9 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo
break;
case ListLayout::Role::String:
{
- QString *value = reinterpret_cast<QString *>(mem);
- if (value->data_ptr() != nullptr)
- data = *value;
+ StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem);
+ if (value->isSet())
+ data = value->toString(owner);
}
break;
case ListLayout::Role::Bool:
@@ -878,15 +953,13 @@ int ListElement::setStringProperty(const ListLayout::Role &role, const QString &
if (role.type == ListLayout::Role::String) {
char *mem = getPropertyMemory(role);
- QString *c = reinterpret_cast<QString *>(mem);
+ StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem);
bool changed;
- if (c->data_ptr() == nullptr) {
- new (mem) QString(s);
+ if (!c->isSet() || c->isTranslation())
changed = true;
- } else {
- changed = c->compare(s) != 0;
- *c = s;
- }
+ else
+ changed = c->asString().compare(s) != 0;
+ c->setString(s);
if (changed)
roleIndex = role.index;
}
@@ -1048,11 +1121,25 @@ int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValu
return roleIndex;
}
+int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::String) {
+ char *mem = getPropertyMemory(role);
+ StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
+ s->setTranslation(b);
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
{
char *mem = getPropertyMemory(role);
- new (mem) QString(s);
+ new (mem) StringOrTranslation(s);
}
void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
@@ -1219,9 +1306,9 @@ void ListElement::destroy(ListLayout *layout)
switch (r.type) {
case ListLayout::Role::String:
{
- QString *string = getStringProperty(r);
+ StringOrTranslation *string = getStringProperty(r);
if (string)
- string->~QString();
+ string->~StringOrTranslation();
}
break;
case ListLayout::Role::List:
@@ -1284,7 +1371,10 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant
roleIndex = setDoubleProperty(role, d.toDouble());
break;
case ListLayout::Role::String:
- roleIndex = setStringProperty(role, d.toString());
+ if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>())
+ roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>());
+ else
+ roleIndex = setStringProperty(role, d.toString());
break;
case ListLayout::Role::Bool:
roleIndex = setBoolProperty(role, d.toBool());
@@ -1332,7 +1422,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d
ListModel *subModel = new ListModel(role.subLayout, nullptr);
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
- o = a->getIndexed(j);
+ o = a->get(j);
subModel->append(o);
}
roleIndex = setListProperty(role, subModel);
@@ -1446,7 +1536,7 @@ void ModelNodeMetaObject::propertyWritten(int index)
return;
QString propName = QString::fromUtf8(name(index));
- QVariant value = operator[](index);
+ const QVariant value = this->value(index);
QV4::Scope scope(m_model->engine());
QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
@@ -1474,29 +1564,37 @@ void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCo
namespace QV4 {
-bool ModelObject::put(Managed *m, String *name, const Value &value)
+bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+ QString propName = id.toQString();
+
ModelObject *that = static_cast<ModelObject*>(m);
ExecutionEngine *eng = that->engine();
const int elementIndex = that->d()->elementIndex();
- const QString propName = name->toQString();
int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng);
if (roleIndex != -1)
that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex));
ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object());
if (mo->initialized())
- mo->emitPropertyNotification(name->toQString().toUtf8());
+ mo->emitPropertyNotification(propName.toUtf8());
return true;
}
-ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ if (!id.isString())
+ return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
+
const ModelObject *that = static_cast<const ModelObject*>(m);
+ Scope scope(that);
+ ScopedString name(scope, id.asStringOrSymbol());
const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name);
if (!role)
- return QObjectWrapper::get(m, name, hasProperty);
+ return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
if (hasProperty)
*hasProperty = true;
@@ -1512,7 +1610,7 @@ ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty
return that->engine()->fromVariant(value);
}
-void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+void ModelObject::virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{
ModelObject *that = static_cast<ModelObject*>(m);
ExecutionEngine *v4 = that->engine();
@@ -1532,7 +1630,7 @@ void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, u
// Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add
// unnecessary entries that relate to the roles used. These just create extra work
// later on as they will just be ignored.
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+ QV4::Object::virtualAdvanceIterator(m, it, name, index, p, attributes);
}
DEFINE_OBJECT_VTABLE(ModelObject);
@@ -1809,6 +1907,7 @@ QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::E
m_listModel = data;
m_engine = engine;
+ m_compilationUnit = owner->m_compilationUnit;
}
QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
@@ -1828,6 +1927,7 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen
ListModel::sync(orig->m_listModel, m_listModel);
m_engine = nullptr;
+ m_compilationUnit = orig->m_compilationUnit;
}
QQmlListModel::~QQmlListModel()
@@ -2244,7 +2344,7 @@ void QQmlListModel::insert(QQmlV4Function *args)
int objectArrayLength = objectArray->getLength();
emitItemsAboutToBeInserted(index, objectArrayLength);
for (int i=0 ; i < objectArrayLength ; ++i) {
- argObject = objectArray->getIndexed(i);
+ argObject = objectArray->get(i);
if (m_dynamicRoles) {
m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
@@ -2356,7 +2456,7 @@ void QQmlListModel::append(QQmlV4Function *args)
emitItemsAboutToBeInserted(index, objectArrayLength);
for (int i=0 ; i < objectArrayLength ; ++i) {
- argObject = objectArray->getIndexed(i);
+ argObject = objectArray->get(i);
if (m_dynamicRoles) {
m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
@@ -2434,7 +2534,7 @@ QQmlV4Handle QQmlListModel::get(int index) const
QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index);
QQmlData *ddata = QQmlData::get(object);
if (ddata->jsWrapper.isNullOrUndefined()) {
- result = scope.engine->memoryManager->allocObject<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this));
+ result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this));
// Keep track of the QObjectWrapper in persistent value storage
ddata->jsWrapper.set(scope.engine, result);
} else {
@@ -2631,7 +2731,9 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp
} else {
QVariant value;
- if (binding->evaluatesToString()) {
+ if (binding->isTranslationBinding()) {
+ value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding);
+ } else if (binding->evaluatesToString()) {
value = binding->valueAsString(qmlUnit);
} else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
value = binding->valueAsNumber();
@@ -2693,6 +2795,7 @@ void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::Compila
QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
rv->m_engine = qmlEngine(rv)->handle();
+ rv->m_compilationUnit = compilationUnit;
const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data;
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h
index 0c0859dc80..56a1a95a02 100644
--- a/src/qml/types/qqmllistmodel_p.h
+++ b/src/qml/types/qqmllistmodel_p.h
@@ -64,6 +64,8 @@
#include <private/qv4engine_p.h>
#include <private/qpodvector_p.h>
+QT_REQUIRE_CONFIG(qml_list_model);
+
QT_BEGIN_NAMESPACE
@@ -122,6 +124,7 @@ private:
friend class ListElement;
friend class DynamicRoleModelNode;
friend class DynamicRoleModelNodeMetaObject;
+ friend struct StringOrTranslation;
// Constructs a flat list model for a worker agent
QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent);
@@ -133,6 +136,7 @@ private:
QQmlListModelWorkerAgent *m_agent;
mutable QV4::ExecutionEngine *m_engine;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
bool m_mainThread;
bool m_primary;
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
index ad5e94c909..e4a850e8a5 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -57,6 +57,8 @@
#include <private/qv4qobjectwrapper_p.h>
#include <qqml.h>
+QT_REQUIRE_CONFIG(qml_list_model);
+
QT_BEGIN_NAMESPACE
@@ -142,16 +144,6 @@ protected:
private:
using QQmlOpenMetaObject::setValue;
- void setValue(const QByteArray &name, const QVariant &val, bool force)
- {
- if (force) {
- QVariant existingValue = value(name);
- if (existingValue.isValid()) {
- (*this)[name] = QVariant();
- }
- }
- setValue(name, val);
- }
void emitDirectNotifies(const int *changedRoles, int roleCount);
@@ -181,12 +173,13 @@ struct ModelObject : public QObjectWrapper {
struct ModelObject : public QObjectWrapper
{
- static bool put(Managed *m, String *name, const Value& value);
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
-
V4_OBJECT2(ModelObject, QObjectWrapper)
V4_NEEDS_DESTROY
+
+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 void virtualAdvanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
};
} // namespace QV4
@@ -252,6 +245,22 @@ private:
QStringHash<Role *> roleHash;
};
+struct StringOrTranslation
+{
+ explicit StringOrTranslation(const QString &s);
+ explicit StringOrTranslation(const QV4::CompiledData::Binding *binding);
+ ~StringOrTranslation();
+ bool isSet() const { return d.flag(); }
+ bool isTranslation() const { return d.isT2(); }
+ void setString(const QString &s);
+ void setTranslation(const QV4::CompiledData::Binding *binding);
+ QString toString(const QQmlListModel *owner) const;
+ QString asString() const;
+private:
+ void clear();
+ QBiPointer<QStringData, const QV4::CompiledData::Binding> d;
+};
+
/*!
\internal
*/
@@ -287,6 +296,7 @@ private:
int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f);
+ int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b);
void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
void setDoublePropertyFast(const ListLayout::Role &role, double n);
@@ -301,7 +311,7 @@ private:
QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
ListModel *getListProperty(const ListLayout::Role &role);
- QString *getStringProperty(const ListLayout::Role &role);
+ StringOrTranslation *getStringProperty(const ListLayout::Role &role);
QObject *getQObjectProperty(const ListLayout::Role &role);
QPointer<QObject> *getGuardProperty(const ListLayout::Role &role);
QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h
index 2120f25744..ae2d4b11e0 100644
--- a/src/qml/types/qqmllistmodelworkeragent_p.h
+++ b/src/qml/types/qqmllistmodelworkeragent_p.h
@@ -59,6 +59,8 @@
#include <private/qv8engine_p.h>
+QT_REQUIRE_CONFIG(qml_list_model);
+
QT_BEGIN_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp
index e217b63c6f..d9756704d1 100644
--- a/src/qml/types/qqmlmodelsmodule.cpp
+++ b/src/qml/types/qqmlmodelsmodule.cpp
@@ -39,7 +39,9 @@
#include "qqmlmodelsmodule_p.h"
#include <QtCore/qitemselectionmodel.h>
+#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
+#endif
#if QT_CONFIG(qml_delegate_model)
#include <private/qqmldelegatemodel_p.h>
#endif
@@ -51,10 +53,13 @@ void QQmlModelsModule::defineModule()
{
const char uri[] = "QtQml.Models";
+#if QT_CONFIG(qml_list_model)
qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement");
qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser);
+#endif
#if QT_CONFIG(qml_delegate_model)
qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel");
+ qmlRegisterType<QQmlDelegateModel,12>(uri, 2, 9, "DelegateModel");
qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup");
#endif
qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel");
diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h
index 267828dcdd..4ac4f1c65b 100644
--- a/src/qml/types/qqmlobjectmodel_p.h
+++ b/src/qml/types/qqmlobjectmodel_p.h
@@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE
class QObject;
class QQmlChangeSet;
+class QAbstractItemModel;
class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject
{
@@ -83,6 +84,7 @@ public:
virtual QQmlIncubator::Status incubationStatus(int index) = 0;
virtual int indexOf(QObject *object, QObject *objectContext) const = 0;
+ virtual const QAbstractItemModel *abstractItemModel() const { return nullptr; }
Q_SIGNALS:
void countChanged();
diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp
new file mode 100644
index 0000000000..dcad84cfb8
--- /dev/null
+++ b/src/qml/types/qqmltableinstancemodel.cpp
@@ -0,0 +1,341 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltableinstancemodel_p.h"
+#include "qqmltableinstancemodel_p.h"
+
+#include <QtCore/QTimer>
+
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/private/qqmlcomponent_p.h>
+
+QT_BEGIN_NAMESPACE
+
+const char* kModelItemTag = "_tableinstancemodel_modelItem";
+
+bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem)
+{
+ if (!modelItem->incubationTask)
+ return true;
+
+ const auto status = modelItem->incubationTask->status();
+ return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error);
+}
+
+void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem)
+{
+ Q_ASSERT(modelItem);
+
+ delete modelItem->object;
+ modelItem->object = nullptr;
+
+ if (modelItem->contextData) {
+ modelItem->contextData->invalidate();
+ Q_ASSERT(modelItem->contextData->refCount == 1);
+ modelItem->contextData = nullptr;
+ }
+
+ modelItem->deleteLater();
+}
+
+QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent)
+ : QQmlInstanceModel(*(new QObjectPrivate()), parent)
+ , m_qmlContext(qmlContext)
+ , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList()))
+{
+}
+
+QQmlTableInstanceModel::~QQmlTableInstanceModel()
+{
+ deleteAllFinishedIncubationTasks();
+
+ // If we have model items loaded at this point, it means that
+ // the view is still holding references to them. So basically
+ // the view needs to be deleted first (and thereby release all
+ // its items), before this destructor is run.
+ Q_ASSERT(m_modelItems.isEmpty());
+}
+
+QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode)
+{
+ Q_ASSERT(m_delegate);
+ Q_ASSERT(index >= 0 && index < m_adaptorModel.count());
+ Q_ASSERT(m_qmlContext && m_qmlContext->isValid());
+
+ // Check if we've already created an item for the given index
+ QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr);
+
+ if (!modelItem) {
+ // Create a new model item
+ modelItem = m_adaptorModel.createItem(m_metaType, index);
+ if (!modelItem) {
+ qWarning() << Q_FUNC_INFO << "Adaptor model failed creating a model item";
+ return nullptr;
+ }
+ m_modelItems.insert(index, modelItem);
+ }
+
+ if (modelItem->object) {
+ // The model item has already been incubated. So
+ // just bump the ref-count and return it.
+ modelItem->referenceObject();
+ return modelItem->object;
+ }
+
+ // Guard the model item temporarily so that it's not deleted from
+ // incubatorStatusChanged(), in case the incubation is done synchronously.
+ modelItem->scriptRef++;
+
+ if (modelItem->incubationTask) {
+ // We're already incubating the model item from a previous request. If the previous call requested
+ // the item async, but the current request needs it sync, we need to force-complete the incubation.
+ const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested);
+ if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous)
+ modelItem->incubationTask->forceCompletion();
+ } else {
+ modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode);
+
+ QQmlContextData *ctxt = new QQmlContextData;
+ QQmlContext *creationContext = m_delegate->creationContext();
+ ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()));
+ ctxt->contextObject = modelItem;
+ modelItem->contextData = ctxt;
+
+ QQmlComponentPrivate::get(m_delegate)->incubateObject(
+ modelItem->incubationTask,
+ m_delegate,
+ m_qmlContext->engine(),
+ ctxt,
+ QQmlContextData::get(m_qmlContext));
+ }
+
+ // Remove the temporary guard
+ modelItem->scriptRef--;
+ Q_ASSERT(modelItem->scriptRef == 0);
+
+ if (!isDoneIncubating(modelItem))
+ return nullptr;
+
+ // When incubation is done, the task should be removed
+ Q_ASSERT(!modelItem->incubationTask);
+
+ if (!modelItem->object) {
+ // The object was incubated synchronously (otherwise we would return above). But since
+ // we have no object, the incubation must have failed. And when we have no object, there
+ // should be no object references either. And there should also not be any internal script
+ // refs at this point. So we delete the model item.
+ Q_ASSERT(!modelItem->isObjectReferenced());
+ Q_ASSERT(!modelItem->isReferenced());
+ m_modelItems.remove(modelItem->index);
+ delete modelItem;
+ return nullptr;
+ }
+
+ // Incubation was completed sync and successful
+ modelItem->referenceObject();
+ return modelItem->object;
+}
+
+QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object)
+{
+ Q_ASSERT(object);
+ auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
+ Q_ASSERT(modelItem);
+
+ if (!modelItem->releaseObject())
+ return QQmlDelegateModel::Referenced;
+
+ if (modelItem->isReferenced()) {
+ // We still have an internal reference to this object, which means that we are told to release an
+ // object while the createdItem signal for it is still on the stack. This can happen when objects
+ // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch
+ // up with. The view might then find that the object is no longer visible and should be released.
+ // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers
+ // points of view, it should consider it destroyed.
+ return QQmlDelegateModel::Destroyed;
+ }
+
+ m_modelItems.remove(modelItem->index);
+ modelItem->destroyObject();
+ emit destroyingItem(object);
+
+ delete modelItem;
+ return QQmlInstanceModel::Destroyed;
+}
+
+void QQmlTableInstanceModel::cancel(int index)
+{
+ auto modelItem = m_modelItems.value(index);
+ Q_ASSERT(modelItem);
+
+ // Since the view expects the item to be incubating, there should be
+ // an incubation task. And since the incubation is not done, no-one
+ // should yet have received, and therfore hold a reference to, the object.
+ Q_ASSERT(modelItem->incubationTask);
+ Q_ASSERT(!modelItem->isObjectReferenced());
+
+ m_modelItems.remove(index);
+
+ if (modelItem->object)
+ delete modelItem->object;
+
+ // modelItem->incubationTask will be deleted from the modelItems destructor
+ delete modelItem;
+}
+
+void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
+{
+ QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
+ Q_ASSERT(modelItem->incubationTask);
+
+ modelItem->incubationTask = nullptr;
+ incubationTask->modelItemToIncubate = nullptr;
+
+ if (status == QQmlIncubator::Ready) {
+ // Tag the incubated object with the model item for easy retrieval upon release etc.
+ modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
+
+ // Emit that the item has been created. What normally happens next is that the view
+ // upon receiving the signal asks for the model item once more. And since the item is
+ // now in the map, it will be returned directly.
+ Q_ASSERT(modelItem->object);
+ modelItem->scriptRef++;
+ emit createdItem(modelItem->index, modelItem->object);
+ modelItem->scriptRef--;
+ } else if (status == QQmlIncubator::Error) {
+ qWarning() << "Error incubating delegate:" << incubationTask->errors();
+ }
+
+ if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) {
+ // We have no internal reference to the model item, and the view has no
+ // reference to the incubated object. So just delete the model item.
+ // Note that being here means that the object was incubated _async_
+ // (otherwise modelItem->isReferenced() would be true).
+ m_modelItems.remove(modelItem->index);
+
+ if (modelItem->object) {
+ modelItem->scriptRef++;
+ emit destroyingItem(modelItem->object);
+ modelItem->scriptRef--;
+ Q_ASSERT(!modelItem->isReferenced());
+ }
+
+ deleteModelItemLater(modelItem);
+ }
+
+ deleteIncubationTaskLater(incubationTask);
+}
+
+QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) {
+ const auto modelItem = m_modelItems.value(index, nullptr);
+ if (!modelItem)
+ return QQmlIncubator::Null;
+
+ if (modelItem->incubationTask)
+ return modelItem->incubationTask->status();
+
+ // Since we clear the incubation task when we're done
+ // incubating, it means that the status is Ready.
+ return QQmlIncubator::Ready;
+}
+
+void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
+{
+ // We often need to post-delete incubation tasks, since we cannot
+ // delete them while we're in the middle of an incubation change callback.
+ Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask));
+ m_finishedIncubationTasks.append(incubationTask);
+ if (m_finishedIncubationTasks.count() == 1)
+ QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks);
+}
+
+void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
+{
+ qDeleteAll(m_finishedIncubationTasks);
+ m_finishedIncubationTasks.clear();
+}
+
+QVariant QQmlTableInstanceModel::model() const
+{
+ return m_adaptorModel.model();
+}
+
+void QQmlTableInstanceModel::setModel(const QVariant &model)
+{
+ m_adaptorModel.setModel(model, this, m_qmlContext->engine());
+}
+
+QQmlComponent *QQmlTableInstanceModel::delegate() const
+{
+ return m_delegate;
+}
+
+void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
+{
+ m_delegate = delegate;
+}
+
+const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const
+{
+ return m_adaptorModel.aim();
+}
+
+// --------------------------------------------------------
+
+void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object)
+{
+ modelItemToIncubate->object = object;
+ emit tableInstanceModel->initItem(modelItemToIncubate->index, object);
+}
+
+void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status)
+{
+ if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
+ return;
+
+ // We require the view to cancel any ongoing load
+ // requests before the tableInstanceModel is destructed.
+ Q_ASSERT(tableInstanceModel);
+
+ tableInstanceModel->incubatorStatusChanged(this, status);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h
new file mode 100644
index 0000000000..fb222d8bc4
--- /dev/null
+++ b/src/qml/types/qqmltableinstancemodel_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTABLEINSTANCEMODEL_P_H
+#define QQMLTABLEINSTANCEMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQml/private/qqmldelegatemodel_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTableInstanceModel;
+
+class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask
+{
+public:
+ QQmlTableInstanceModelIncubationTask(
+ QQmlTableInstanceModel *tableInstanceModel
+ , QQmlDelegateModelItem* modelItemToIncubate
+ , IncubationMode mode)
+ : QQDMIncubationTask(nullptr, mode)
+ , modelItemToIncubate(modelItemToIncubate)
+ , tableInstanceModel(tableInstanceModel) {
+ clear();
+ }
+
+ void statusChanged(Status status) override;
+ void setInitialState(QObject *object) override;
+
+ QQmlDelegateModelItem *modelItemToIncubate = nullptr;
+ QQmlTableInstanceModel *tableInstanceModel = nullptr;
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel
+{
+public:
+ QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent = nullptr);
+ ~QQmlTableInstanceModel() override;
+
+ int count() const override { return m_adaptorModel.count(); }
+ int rows() const { return m_adaptorModel.rowCount(); }
+ int columns() const { return m_adaptorModel.columnCount(); }
+
+ bool isValid() const override { return true; }
+
+ QVariant model() const;
+ void setModel(const QVariant &model);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *);
+
+ const QAbstractItemModel *abstractItemModel() const override;
+
+ QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
+ ReleaseFlags release(QObject *) override;
+ void cancel(int) override;
+
+ QQmlIncubator::Status incubationStatus(int index) override;
+
+ QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); }
+ void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); }
+ int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; }
+
+private:
+ QQmlComponent *m_delegate = nullptr;
+ QQmlAdaptorModel m_adaptorModel;
+ QPointer<QQmlContext> m_qmlContext;
+ QQmlDelegateModelItemMetaType *m_metaType;
+
+ QHash<int, QQmlDelegateModelItem *> m_modelItems;
+ QList<QQmlIncubator *> m_finishedIncubationTasks;
+
+ void incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *dmIncubationTask, QQmlIncubator::Status status);
+ void deleteIncubationTaskLater(QQmlIncubator *incubationTask);
+ void deleteAllFinishedIncubationTasks();
+
+ static bool isDoneIncubating(QQmlDelegateModelItem *modelItem);
+ static void deleteModelItemLater(QQmlDelegateModelItem *modelItem);
+
+ friend class QQmlTableInstanceModelIncubationTask;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTABLEINSTANCEMODEL_P_H
diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h
index d597869994..0160e97a2f 100644
--- a/src/qml/types/qqmltimer_p.h
+++ b/src/qml/types/qqmltimer_p.h
@@ -57,6 +57,8 @@
#include <private/qtqmlglobal_p.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class QQmlTimerPrivate;
diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp
index 98f819337b..f8879160b2 100644
--- a/src/qml/types/qquickworkerscript.cpp
+++ b/src/qml/types/qquickworkerscript.cpp
@@ -37,9 +37,12 @@
**
****************************************************************************/
+#include "qtqmlglobal_p.h"
#include "qquickworkerscript_p.h"
+#if QT_CONFIG(qml_list_model)
#include "qqmllistmodel_p.h"
#include "qqmllistmodelworkeragent_p.h"
+#endif
#include <private/qqmlengine_p.h>
#include <private/qqmlexpression_p.h>
@@ -201,7 +204,7 @@ private:
};
QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent)
- : QV8Engine(nullptr, new QV4::ExecutionEngine), p(parent)
+ : QV8Engine(new QV4::ExecutionEngine), p(parent)
#if QT_CONFIG(qml_network)
, accessManager(nullptr)
#endif
@@ -241,14 +244,13 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init()
QV4::Scope scope(m_v4Engine);
QV4::ExecutionContext *globalContext = scope.engine->rootContext();
- onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
+ onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::ContextType::Global, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
Q_ASSERT(!scope.engine->hasException);
- QV4::Script createsendscript(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
+ QV4::Script createsendscript(globalContext, QV4::Compiler::ContextType::Global, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
QV4::ScopedFunctionObject createsendconstructor(scope, createsendscript.run());
Q_ASSERT(!scope.engine->hasException);
QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage")));
- QV4::ScopedValue function(scope, QV4::FunctionObject::createBuiltinFunction(globalContext, name,
- QQuickWorkerScriptEnginePrivate::method_sendMessage));
+ QV4::ScopedValue function(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1));
QV4::JSCallData jsCallData(scope, 1);
jsCallData->args[0] = function;
*jsCallData->thisObject = m_v4Engine->global();
diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri
index 8bcbd6e544..6a74d70a0e 100644
--- a/src/qml/types/types.pri
+++ b/src/qml/types/types.pri
@@ -1,28 +1,36 @@
SOURCES += \
$$PWD/qqmlbind.cpp \
$$PWD/qqmlconnections.cpp \
- $$PWD/qqmllistmodel.cpp \
- $$PWD/qqmllistmodelworkeragent.cpp \
$$PWD/qqmlmodelsmodule.cpp \
$$PWD/qqmlmodelindexvaluetype.cpp \
$$PWD/qqmlobjectmodel.cpp \
$$PWD/qquickpackage.cpp \
$$PWD/qquickworkerscript.cpp \
- $$PWD/qqmlinstantiator.cpp
+ $$PWD/qqmlinstantiator.cpp \
+ $$PWD/qqmltableinstancemodel.cpp
HEADERS += \
$$PWD/qqmlbind_p.h \
$$PWD/qqmlconnections_p.h \
- $$PWD/qqmllistmodel_p.h \
- $$PWD/qqmllistmodel_p_p.h \
- $$PWD/qqmllistmodelworkeragent_p.h \
$$PWD/qqmlmodelsmodule_p.h \
$$PWD/qqmlmodelindexvaluetype_p.h \
$$PWD/qqmlobjectmodel_p.h \
$$PWD/qquickpackage_p.h \
$$PWD/qquickworkerscript_p.h \
$$PWD/qqmlinstantiator_p.h \
- $$PWD/qqmlinstantiator_p_p.h
+ $$PWD/qqmlinstantiator_p_p.h \
+ $$PWD/qqmltableinstancemodel_p.h
+
+qtConfig(qml-list-model) {
+ SOURCES += \
+ $$PWD/qqmllistmodel.cpp \
+ $$PWD/qqmllistmodelworkeragent.cpp
+
+ HEADERS += \
+ $$PWD/qqmllistmodel_p.h \
+ $$PWD/qqmllistmodel_p_p.h \
+ $$PWD/qqmllistmodelworkeragent_p.h
+}
qtConfig(qml-delegate-model) {
SOURCES += \
@@ -33,7 +41,7 @@ qtConfig(qml-delegate-model) {
$$PWD/qqmldelegatemodel_p_p.h
}
-qtConfig(animation) {
+qtConfig(qml-animation) {
SOURCES += \
$$PWD/qqmltimer.cpp
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index b4bebb9d5d..24d9c14611 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -96,7 +96,7 @@ public:
QQmlDMCachedModelData(
QQmlDelegateModelItemMetaType *metaType,
VDMModelDelegateDataType *dataType,
- int index);
+ int index, int row, int column);
int metaCall(QMetaObject::Call call, int id, void **arguments);
@@ -228,8 +228,8 @@ public:
QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName)));
QV4::ExecutionContext *global = v4->rootContext();
- QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property));
- QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property));
+ QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property));
+ QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property));
p->setGetter(g);
p->setSetter(s);
proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
@@ -262,9 +262,8 @@ public:
bool hasModelData;
};
-QQmlDMCachedModelData::QQmlDMCachedModelData(
- QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index)
- : QQmlDelegateModelItem(metaType, index)
+QQmlDMCachedModelData::QQmlDMCachedModelData(QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index, int row, int column)
+ : QQmlDelegateModelItem(metaType, index, row, column)
, type(dataType)
{
if (index == -1)
@@ -326,13 +325,12 @@ void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value)
}
}
-bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx)
+bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx)
{
if (index == -1) {
Q_ASSERT(idx >= 0);
- index = idx;
cachedData.clear();
- emit modelIndexChanged();
+ setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx));
const QMetaObject *meta = metaObject();
const int propertyCount = type->propertyRoles.count();
for (int i = 0; i < propertyCount; ++i)
@@ -400,12 +398,13 @@ class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData
{
Q_OBJECT
Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
+
public:
QQmlDMAbstractItemModelData(
QQmlDelegateModelItemMetaType *metaType,
VDMModelDelegateDataType *dataType,
- int index)
- : QQmlDMCachedModelData(metaType, dataType, index)
+ int index, int row, int column)
+ : QQmlDMCachedModelData(metaType, dataType, index, row, column)
{
}
@@ -413,7 +412,7 @@ public:
{
if (index >= 0 && *type->model) {
const QAbstractItemModel * const model = type->model->aim();
- return model->hasChildren(model->index(index, 0, type->model->rootIndex));
+ return model->hasChildren(model->index(row, column, type->model->rootIndex));
} else {
return false;
}
@@ -421,13 +420,13 @@ public:
QVariant value(int role) const override
{
- return type->model->aim()->index(index, 0, type->model->rootIndex).data(role);
+ return type->model->aim()->index(row, column, type->model->rootIndex).data(role);
}
void setValue(int role, const QVariant &value) override
{
type->model->aim()->setData(
- type->model->aim()->index(index, 0, type->model->rootIndex), value, role);
+ type->model->aim()->index(row, column, type->model->rootIndex), value, role);
}
QV4::ReturnedValue get() override
@@ -438,8 +437,8 @@ public:
}
QV4::Scope scope(v4);
QV4::ScopedObject proto(scope, type->prototype.value());
- QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocObject<QQmlDelegateModelItemObject>(this));
- o->setPrototype(proto);
+ QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
+ o->setPrototypeOf(proto);
++scriptRef;
return o.asReturnedValue();
}
@@ -453,31 +452,18 @@ public:
{
}
- int count(const QQmlAdaptorModel &model) const override
+ int rowCount(const QQmlAdaptorModel &model) const override
{
return model.aim()->rowCount(model.rootIndex);
}
- void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const override
- {
- QAbstractItemModel * const aim = model.aim();
- if (aim && vdm) {
- QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)),
- vdm, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- vdm, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- vdm, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- vdm, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
- QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- vdm, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
- QObject::disconnect(aim, SIGNAL(modelReset()),
- vdm, SLOT(_q_modelReset()));
- QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- vdm, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
- }
+ int columnCount(const QQmlAdaptorModel &model) const override
+ {
+ return model.aim()->columnCount(model.rootIndex);
+ }
+ void cleanup(QQmlAdaptorModel &) const override
+ {
const_cast<VDMAbstractItemModelDataType *>(this)->release();
}
@@ -485,9 +471,9 @@ public:
{
QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8());
if (it != roleNames.end()) {
- return model.aim()->index(index, 0, model.rootIndex).data(*it);
+ return model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex).data(*it);
} else if (role == QLatin1String("hasModelChildren")) {
- return QVariant(model.aim()->hasChildren(model.aim()->index(index, 0, model.rootIndex)));
+ return QVariant(model.aim()->hasChildren(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex)));
} else {
return QVariant();
}
@@ -503,7 +489,7 @@ public:
QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
{
return model
- ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex))
+ ? QVariant::fromValue(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex))
: QVariant();
}
@@ -521,12 +507,12 @@ public:
QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &model,
QQmlDelegateModelItemMetaType *metaType,
- int index) const override
+ int index, int row, int column) const override
{
VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
if (!metaObject)
dataType->initializeMetaType(model);
- return new QQmlDMAbstractItemModelData(metaType, dataType, index);
+ return new QQmlDMAbstractItemModelData(metaType, dataType, index, row, column);
}
void initializeMetaType(QQmlAdaptorModel &model)
@@ -567,8 +553,8 @@ class QQmlDMListAccessorData : public QQmlDelegateModelItem
Q_OBJECT
Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
public:
- QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, const QVariant &value)
- : QQmlDelegateModelItem(metaType, index)
+ QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, int row, int column, const QVariant &value)
+ : QQmlDelegateModelItem(metaType, index, row, column)
, cachedData(value)
{
}
@@ -613,9 +599,9 @@ public:
{
QQmlAdaptorModelEngineData *data = engineData(v4);
QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(this));
+ QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
QV4::ScopedObject p(scope, data->listItemProto.value());
- o->setPrototype(p);
+ o->setPrototypeOf(p);
++scriptRef;
return o.asReturnedValue();
}
@@ -653,11 +639,16 @@ class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors
public:
inline VDMListDelegateDataType() {}
- int count(const QQmlAdaptorModel &model) const override
+ int rowCount(const QQmlAdaptorModel &model) const override
{
return model.list.count();
}
+ int columnCount(const QQmlAdaptorModel &) const override
+ {
+ return 1;
+ }
+
QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
{
return role == QLatin1String("modelData")
@@ -668,11 +659,11 @@ public:
QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &model,
QQmlDelegateModelItemMetaType *metaType,
- int index) const override
+ int index, int row, int column) const override
{
return new QQmlDMListAccessorData(
metaType,
- index,
+ index, row, column,
index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant());
}
};
@@ -691,7 +682,7 @@ public:
QQmlDMObjectData(
QQmlDelegateModelItemMetaType *metaType,
VDMObjectDelegateDataType *dataType,
- int index,
+ int index, int row, int column,
QObject *object);
QObject *modelData() const { return object; }
@@ -737,11 +728,16 @@ public:
free(metaObject);
}
- int count(const QQmlAdaptorModel &model) const override
+ int rowCount(const QQmlAdaptorModel &model) const override
{
return model.list.count();
}
+ int columnCount(const QQmlAdaptorModel &) const override
+ {
+ return 1;
+ }
+
QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
{
if (QObject *object = model.list.at(index).value<QObject *>())
@@ -752,13 +748,13 @@ public:
QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &model,
QQmlDelegateModelItemMetaType *metaType,
- int index) const override
+ int index, int row, int column) const override
{
VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this);
if (!metaObject)
dataType->initializeMetaType(model);
return index >= 0 && index < model.list.count()
- ? new QQmlDMObjectData(metaType, dataType, index, qvariant_cast<QObject *>(model.list.at(index)))
+ ? new QQmlDMObjectData(metaType, dataType, index, row, column, qvariant_cast<QObject *>(model.list.at(index)))
: nullptr;
}
@@ -769,7 +765,7 @@ public:
metaObject = builder.toMetaObject();
}
- void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const override
+ void cleanup(QQmlAdaptorModel &) const override
{
const_cast<VDMObjectDelegateDataType *>(this)->release();
}
@@ -872,12 +868,11 @@ public:
VDMObjectDelegateDataType *m_type;
};
-QQmlDMObjectData::QQmlDMObjectData(
- QQmlDelegateModelItemMetaType *metaType,
+QQmlDMObjectData::QQmlDMObjectData(QQmlDelegateModelItemMetaType *metaType,
VDMObjectDelegateDataType *dataType,
- int index,
+ int index, int row, int column,
QObject *object)
- : QQmlDelegateModelItem(metaType, index)
+ : QQmlDelegateModelItem(metaType, index, row, column)
, object(object)
{
new QQmlDMObjectDataMetaObject(this, dataType);
@@ -904,50 +899,34 @@ QQmlAdaptorModel::~QQmlAdaptorModel()
accessors->cleanup(*this);
}
-void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine)
+void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine)
{
- accessors->cleanup(*this, vdm);
+ accessors->cleanup(*this);
list.setList(variant, engine);
if (QObject *object = qvariant_cast<QObject *>(list.list())) {
- setObject(object);
- if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(object)) {
+ setObject(object, parent);
+ if (qobject_cast<QAbstractItemModel *>(object))
accessors = new VDMAbstractItemModelDataType(this);
-
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- vdm, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(modelReset()),
- vdm, QQmlDelegateModel, SLOT(_q_modelReset()));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- vdm, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
- } else {
+ else
accessors = new VDMObjectDelegateDataType;
- }
} else if (list.type() == QQmlListAccessor::ListProperty) {
- setObject(static_cast<const QQmlListReference *>(variant.constData())->object());
+ setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent);
accessors = new VDMObjectDelegateDataType;
} else if (list.type() != QQmlListAccessor::Invalid
&& list.type() != QQmlListAccessor::Instance) { // Null QObject
- setObject(nullptr);
+ setObject(nullptr, parent);
accessors = &qt_vdm_list_accessors;
} else {
- setObject(nullptr);
+ setObject(nullptr, parent);
accessors = &qt_vdm_null_accessors;
}
}
-void QQmlAdaptorModel::invalidateModel(QQmlDelegateModel *vdm)
+void QQmlAdaptorModel::invalidateModel()
{
- accessors->cleanup(*this, vdm);
+ accessors->cleanup(*this);
accessors = &qt_vdm_null_accessors;
// Don't clear the model object as we still need the guard to clear the list variant if the
// object is destroyed.
@@ -958,6 +937,38 @@ bool QQmlAdaptorModel::isValid() const
return accessors != &qt_vdm_null_accessors;
}
+int QQmlAdaptorModel::count() const
+{
+ return rowCount() * columnCount();
+}
+
+int QQmlAdaptorModel::rowCount() const
+{
+ return qMax(0, accessors->rowCount(*this));
+}
+
+int QQmlAdaptorModel::columnCount() const
+{
+ return qMax(isValid() ? 1 : 0, accessors->columnCount(*this));
+}
+
+int QQmlAdaptorModel::rowAt(int index) const
+{
+ int count = rowCount();
+ return count <= 0 ? -1 : index % count;
+}
+
+int QQmlAdaptorModel::columnAt(int index) const
+{
+ int count = rowCount();
+ return count <= 0 ? -1 : index / count;
+}
+
+int QQmlAdaptorModel::indexAt(int row, int column) const
+{
+ return row + (column * rowCount());
+}
+
void QQmlAdaptorModel::objectDestroyed(QObject *)
{
setModel(QVariant(), nullptr, nullptr);
diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h
index b152a886a5..b834704163 100644
--- a/src/qml/util/qqmladaptormodel_p.h
+++ b/src/qml/util/qqmladaptormodel_p.h
@@ -56,6 +56,7 @@
#include "private/qqmllistaccessor_p.h"
#include <private/qqmlguard_p.h>
+#include <private/qqmlnullablevalue_p.h>
QT_REQUIRE_CONFIG(qml_delegate_model);
@@ -67,7 +68,7 @@ class QQmlDelegateModel;
class QQmlDelegateModelItem;
class QQmlDelegateModelItemMetaType;
-class QQmlAdaptorModel : public QQmlGuard<QObject>
+class Q_QML_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject>
{
public:
class Accessors
@@ -75,8 +76,9 @@ public:
public:
inline Accessors() {}
virtual ~Accessors();
- virtual int count(const QQmlAdaptorModel &) const { return 0; }
- virtual void cleanup(QQmlAdaptorModel &, QQmlDelegateModel * = nullptr) const {}
+ virtual int rowCount(const QQmlAdaptorModel &) const { return 0; }
+ virtual int columnCount(const QQmlAdaptorModel &) const { return 0; }
+ virtual void cleanup(QQmlAdaptorModel &) const {}
virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const {
return QVariant(); }
@@ -84,7 +86,7 @@ public:
virtual QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &,
QQmlDelegateModelItemMetaType *,
- int) const { return nullptr; }
+ int, int, int) const { return nullptr; }
virtual bool notify(
const QQmlAdaptorModel &,
@@ -112,19 +114,25 @@ public:
~QQmlAdaptorModel();
inline QVariant model() const { return list.list(); }
- void setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine);
- void invalidateModel(QQmlDelegateModel *vdm);
+ void setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine);
+ void invalidateModel();
bool isValid() const;
-
+ int count() const;
+ int rowCount() const;
+ int columnCount() const;
+ int rowAt(int index) const;
+ int columnAt(int index) const;
+ int indexAt(int row, int column) const;
+
+ inline bool adaptsAim() const { return qobject_cast<QAbstractItemModel *>(object()); }
inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); }
inline const QAbstractItemModel *aim() const { return static_cast<const QAbstractItemModel *>(object()); }
- inline int count() const { return qMax(0, accessors->count(*this)); }
inline QVariant value(int index, const QString &role) const {
return accessors->value(*this, index, role); }
inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, int index) {
- return accessors->createItem(*this, metaType, index); }
+ return accessors->createItem(*this, metaType, index, rowAt(index), columnAt(index)); }
inline bool hasProxyObject() const {
return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; }
diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp
index 578c05086f..3f78ca6b69 100644
--- a/src/qml/util/qqmlpropertymap.cpp
+++ b/src/qml/util/qqmlpropertymap.cpp
@@ -122,7 +122,7 @@ QVariant QQmlPropertyMapMetaObject::propertyWriteValue(int index, const QVariant
void QQmlPropertyMapMetaObject::propertyWritten(int index)
{
- priv->emitChanged(priv->propertyName(index), operator[](index));
+ priv->emitChanged(priv->propertyName(index), value(index));
}
void QQmlPropertyMapMetaObject::propertyCreated(int, QMetaPropertyBuilder &b)
@@ -311,7 +311,7 @@ QVariant &QQmlPropertyMap::operator[](const QString &key)
if (!d->keys.contains(key))
insert(key, QVariant());//force creation -- needed below
- return (*(d->mo))[utf8key];
+ return d->mo->valueRef(utf8key);
}
/*!