aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2018-08-30 01:00:07 +0200
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2018-08-30 01:00:07 +0200
commit5ff4facd8efc41b30b5ffa5abb6a12406685ca0b (patch)
tree057d345a90e04dc222eed5cc0e17fce86605b842
parent120bf8a5d1769279d4e9d8378543f0adfc4a50d0 (diff)
parentf738abe53ed96d0d4e29290bc04b270cfc74569b (diff)
Merge remote-tracking branch 'origin/5.12' into dev
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp6
-rw-r--r--src/qml/compiler/qv4codegen.cpp62
-rw-r--r--src/qml/compiler/qv4codegen_p.h14
-rw-r--r--src/qml/compiler/qv4compileddata_p.h11
-rw-r--r--src/qml/compiler/qv4compiler.cpp5
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp91
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h13
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp9
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp10
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h4
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp24
-rw-r--r--src/qml/jit/qv4baselineassembler_p.h1
-rw-r--r--src/qml/jit/qv4baselinejit.cpp13
-rw-r--r--src/qml/jit/qv4baselinejit_p.h2
-rw-r--r--src/qml/jit/qv4jithelpers.cpp10
-rw-r--r--src/qml/jit/qv4jithelpers_p.h1
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4context.cpp4
-rw-r--r--src/qml/jsruntime/qv4context_p.h6
-rw-r--r--src/qml/jsruntime/qv4engine.cpp10
-rw-r--r--src/qml/jsruntime/qv4engine_p.h8
-rw-r--r--src/qml/jsruntime/qv4estable.cpp26
-rw-r--r--src/qml/jsruntime/qv4estable_p.h4
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4global_p.h6
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h1
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp202
-rw-r--r--src/qml/jsruntime/qv4mapobject_p.h35
-rw-r--r--src/qml/jsruntime/qv4module.cpp34
-rw-r--r--src/qml/jsruntime/qv4module_p.h1
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp6
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp3
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp4
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp27
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h2
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp99
-rw-r--r--src/qml/jsruntime/qv4setobject_p.h37
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h9
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp7
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp22
-rw-r--r--src/qml/memory/qv4mm.cpp37
-rw-r--r--src/qml/memory/qv4mm_p.h5
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations281
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp2
-rw-r--r--tests/auto/qml/qml.pro3
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp14
-rw-r--r--tests/auto/qml/v4misc/tst_v4misc.cpp245
48 files changed, 809 insertions, 620 deletions
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
index e2533a0505..181594010d 100644
--- a/src/qml/compiler/qv4bytecodehandler.cpp
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -290,6 +290,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
addLabel(code - start + offset);
COLLECTOR_END_INSTR(UnwindToLabel)
+ COLLECTOR_BEGIN_INSTR(DeadTemporalZoneCheck)
+ COLLECTOR_END_INSTR(DeadTemporalZoneCheck)
+
COLLECTOR_BEGIN_INSTR(ThrowException)
COLLECTOR_END_INSTR(ThrowException)
@@ -519,6 +522,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
COLLECTOR_END_INSTR(Debug)
#endif // QT_NO_QML_DEBUGGER
+ COLLECTOR_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ COLLECTOR_END_INSTR(InitializeBlockDeadTemporalZone)
+
COLLECTOR_BEGIN_INSTR(LoadQmlContext)
COLLECTOR_END_INSTR(LoadQmlContext)
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 734de853d1..412d787d11 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -47,6 +47,7 @@
#include <QtCore/QBitArray>
#include <QtCore/QLinkedList>
#include <QtCore/QStack>
+#include <QScopeGuard>
#include <private/qqmljsast_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
@@ -501,8 +502,14 @@ void Codegen::variableDeclaration(PatternElement *ast)
{
RegisterScope scope(this);
- if (!ast->initializer)
+ if (!ast->initializer) {
+ if (ast->isLexicallyScoped()) {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ Reference varToStore = targetForPatternElement(ast);
+ varToStore.storeConsumeAccumulator();
+ }
return;
+ }
initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true);
}
@@ -516,7 +523,7 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast)
Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p)
{
if (!p->bindingIdentifier.isNull())
- return referenceForName(p->bindingIdentifier.toString(), true);
+ return referenceForName(p->bindingIdentifier.toString(), true, p->firstSourceLocation());
if (!p->bindingTarget || p->destructuringPattern())
return Codegen::Reference::fromStackSlot(this);
Reference lhs = expression(p->bindingTarget);
@@ -2235,9 +2242,9 @@ bool Codegen::visit(FunctionExpression *ast)
return false;
}
-Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
+Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, const SourceLocation &accessLocation)
{
- Context::ResolvedName resolved = _context->resolveName(name);
+ Context::ResolvedName resolved = _context->resolveName(name, accessLocation);
if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack
|| resolved.type == Context::ResolvedName::Import) {
@@ -2258,6 +2265,8 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
r.isVolatile = true;
r.isArgOrEval = resolved.isArgOrEval;
r.isReferenceToConst = resolved.isConst;
+ r.requiresTDZCheck = resolved.requiresTDZCheck;
+ r.name = name; // used to show correct name at run-time when TDZ check fails.
return r;
}
@@ -2293,7 +2302,7 @@ bool Codegen::visit(IdentifierExpression *ast)
if (hasError)
return false;
- _expr.setResult(referenceForName(ast->name.toString(), false));
+ _expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
return false;
}
@@ -2321,11 +2330,10 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
if (hasError)
return;
- if (base.isSuper()) {
- Reference::fromStackSlot(this, CallData::Function).loadInAccumulator();
- } else {
+ if (base.isSuper())
+ Reference::fromStackSlot(this, CallData::NewTarget).loadInAccumulator();
+ else
constructor.loadInAccumulator();
- }
if (calldata.hasSpread) {
Instruction::ConstructWithSpread create;
@@ -3102,7 +3110,7 @@ bool Codegen::visit(ForEachStatement *ast)
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.
+ // This block should define a temporal dead zone for those variables.
{
RegisterScope innerScope(this);
ControlFlowBlock controlFlow(this, ast);
@@ -3818,7 +3826,7 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
scope = other.scope;
break;
case Name:
- name = other.name;
+ // name is always copied
break;
case Member:
propertyBase = other.propertyBase;
@@ -3848,6 +3856,8 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
codegen = other.codegen;
isReadonly = other.isReadonly;
isReferenceToConst = other.isReferenceToConst;
+ name = other.name;
+ requiresTDZCheck = other.requiresTDZCheck;
stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument;
isVolatile = other.isVolatile;
global = other.global;
@@ -3969,10 +3979,10 @@ void Codegen::Reference::storeOnStack(int slotIndex) const
Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
{
- if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile))
+ if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile) && !requiresTDZCheck)
return *this;
- if (isStackSlot()) { // temp-to-temp move
+ if (isStackSlot() && !requiresTDZCheck) { // temp-to-temp move
Reference dest = Reference::fromStackSlot(codegen, slotIndex);
Instruction::MoveReg move;
move.srcReg = stackSlot();
@@ -4130,6 +4140,22 @@ void Codegen::Reference::storeAccumulator() const
void Codegen::Reference::loadInAccumulator() const
{
+ auto tdzCheck = [this](bool requiresCheck){
+ if (!requiresCheck)
+ return;
+ Instruction::DeadTemporalZoneCheck check;
+ check.name = codegen->registerString(name);
+ codegen->bytecodeGenerator->addInstruction(check);
+ };
+ auto tdzCheckStackSlot = [this, tdzCheck](Moth::StackSlot slot, bool requiresCheck){
+ if (!requiresCheck)
+ return;
+ Instruction::LoadReg load;
+ load.reg = slot;
+ codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(true);
+ };
+
switch (type) {
case Accumulator:
return;
@@ -4137,6 +4163,7 @@ void Codegen::Reference::loadInAccumulator() const
Q_UNREACHABLE();
return;
case SuperProperty:
+ tdzCheckStackSlot(property, subscriptRequiresTDZCheck);
Instruction::LoadSuperProperty load;
load.property = property.stackSlot();
codegen->bytecodeGenerator->addInstruction(load);
@@ -4183,6 +4210,7 @@ QT_WARNING_POP
Instruction::LoadReg load;
load.reg = stackSlot();
codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(requiresTDZCheck);
} return;
case ScopedLocal: {
if (!scope) {
@@ -4195,6 +4223,7 @@ QT_WARNING_POP
load.scope = scope;
codegen->bytecodeGenerator->addInstruction(load);
}
+ tdzCheck(requiresTDZCheck);
return;
}
case Name:
@@ -4223,13 +4252,13 @@ QT_WARNING_POP
}
return;
case Member:
+ propertyBase.loadInAccumulator();
+ tdzCheck(requiresTDZCheck);
if (!disable_lookups && codegen->useFastLookups) {
- propertyBase.loadInAccumulator();
Instruction::GetLookup load;
load.index = codegen->registerGetterLookup(propertyNameIndex);
codegen->bytecodeGenerator->addInstruction(load);
} else {
- propertyBase.loadInAccumulator();
Instruction::LoadProperty load;
load.name = propertyNameIndex;
codegen->bytecodeGenerator->addInstruction(load);
@@ -4239,9 +4268,12 @@ QT_WARNING_POP
Instruction::LoadImport load;
load.index = index;
codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(requiresTDZCheck);
} return;
case Subscript: {
+ tdzCheckStackSlot(elementBase, requiresTDZCheck);
elementSubscript.loadInAccumulator();
+ tdzCheck(subscriptRequiresTDZCheck);
Instruction::LoadElement load;
load.base = elementBase;
codegen->bytecodeGenerator->addInstruction(load);
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 516f506396..adea8b3009 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -217,9 +217,10 @@ public:
case Name:
case Member:
case Subscript:
+ case SuperProperty:
return true;
default:
- return false;
+ return requiresTDZCheck;
}
}
bool isConstant() const { return type == Const; }
@@ -294,12 +295,14 @@ public:
Reference r(baseRef.codegen, Member);
r.propertyBase = baseRef.asRValue();
r.propertyNameIndex = r.codegen->registerString(name);
+ r.requiresTDZCheck = baseRef.requiresTDZCheck;
return r;
}
static Reference fromSuperProperty(const Reference &property) {
Q_ASSERT(property.isStackSlot());
Reference r(property.codegen, SuperProperty);
r.property = property.stackSlot();
+ r.subscriptRequiresTDZCheck = property.requiresTDZCheck;
return r;
}
static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) {
@@ -307,6 +310,8 @@ public:
Reference r(baseRef.codegen, Subscript);
r.elementBase = baseRef.stackSlot();
r.elementSubscript = subscript.asRValue();
+ r.requiresTDZCheck = baseRef.requiresTDZCheck;
+ r.subscriptRequiresTDZCheck = subscript.requiresTDZCheck;
return r;
}
static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) {
@@ -334,6 +339,9 @@ public:
static Reference fromThis(Codegen *cg) {
Reference r = fromStackSlot(cg, CallData::This);
r.isReadonly = true;
+ // ### Optimize this. Functions that are not derived constructors or arrow functions can't have an
+ // empty this object
+ r.requiresTDZCheck = true;
return r;
}
@@ -394,6 +402,8 @@ public:
mutable bool isArgOrEval = false;
bool isReadonly = false;
bool isReferenceToConst = false;
+ bool requiresTDZCheck = false;
+ bool subscriptRequiresTDZCheck = false;
bool stackSlotIsLocalOrArgument = false;
bool isVolatile = false;
bool global = false;
@@ -684,7 +694,7 @@ public:
void handleTryFinally(AST::TryStatement *ast);
- Reference referenceForName(const QString &name, bool lhs);
+ Reference referenceForName(const QString &name, bool lhs, const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation());
QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true);
static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index dca38eae54..1f6c591ada 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -237,6 +237,8 @@ struct Block
{
quint32_le nLocals;
quint32_le localsOffset;
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le padding;
const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
@@ -251,7 +253,7 @@ struct Block
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Block) == 8, "Block 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(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// 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.
@@ -276,6 +278,10 @@ struct Function
quint16_le nLineNumbers;
size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le firstTemporalDeadZoneRegister;
+ quint16_le sizeOfRegisterTemporalDeadZone;
+ quint16_le nRegisters;
Location location;
// Qml Extensions Begin
@@ -293,7 +299,6 @@ struct Function
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
- quint16_le nRegisters;
// quint32 formalsIndex[nFormals]
// quint32 localsIndex[nLocals]
@@ -326,7 +331,7 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 48, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Method {
enum Type {
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 4a726c366d..b6e5504c24 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -398,6 +398,10 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
+ function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
+ function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
+ function->firstTemporalDeadZoneRegister = irFunction->firstTemporalDeadZoneRegister;
+
function->nLocals = irFunction->locals.size();
function->localsOffset = currentOffset;
currentOffset += function->nLocals * sizeof(quint32);
@@ -532,6 +536,7 @@ void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context
quint32 currentOffset = sizeof(QV4::CompiledData::Block);
currentOffset = (currentOffset + 7) & ~quint32(0x7);
+ block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
block->nLocals = irBlock->locals.size();
block->localsOffset = currentOffset;
currentOffset += block->nLocals * sizeof(quint32);
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 09be19e427..4ee6d2c179 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -71,7 +71,22 @@ Context *Module::newContext(Node *node, Context *parent, ContextType contextType
return c;
}
-bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function)
+bool Context::Member::requiresTDZCheck(const SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const
+{
+ if (!isLexicallyScoped())
+ return false;
+
+ if (accessAcrossContextBoundaries)
+ return true;
+
+ if (!accessLocation.isValid() || !endOfInitializerLocation.isValid())
+ return true;
+
+ return accessLocation.begin() < endOfInitializerLocation.end();
+}
+
+bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function,
+ const QQmlJS::AST::SourceLocation &endOfInitializer)
{
// ### can this happen?
if (name.isEmpty())
@@ -96,17 +111,18 @@ bool Context::addLocalVar(const QString &name, Context::MemberType type, Variabl
// hoist var declarations to the function level
if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
- return parent->addLocalVar(name, type, scope, function);
+ return parent->addLocalVar(name, type, scope, function, endOfInitializer);
Member m;
m.type = type;
m.function = function;
m.scope = scope;
+ m.endOfInitializerLocation = endOfInitializer;
members.insert(name, m);
return true;
}
-Context::ResolvedName Context::resolveName(const QString &name)
+Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation)
{
int scope = 0;
Context *c = this;
@@ -126,6 +142,7 @@ Context::ResolvedName Context::resolveName(const QString &name)
result.scope = scope;
result.index = m.index;
result.isConst = (m.scope == VariableScope::Const);
+ result.requiresTDZCheck = m.requiresTDZCheck(accessLocation, c != this);
if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
result.isArgOrEval = true;
return result;
@@ -162,6 +179,8 @@ Context::ResolvedName Context::resolveName(const QString &name)
result.index = i;
result.type = ResolvedName::Import;
result.isConst = true;
+ // We don't know at compile time whether the imported value is let/const or not.
+ result.requiresTDZCheck = true;
return result;
}
}
@@ -209,6 +228,13 @@ void Context::emitBlockHeader(Codegen *codegen)
}
}
+ if (contextType == ContextType::Block && sizeOfRegisterTemporalDeadZone > 0) {
+ Instruction::InitializeBlockDeadTemporalZone tdzInit;
+ tdzInit.firstReg = registerOffset + nRegisters - sizeOfRegisterTemporalDeadZone;
+ tdzInit.count = sizeOfRegisterTemporalDeadZone;
+ bytecodeGenerator->addInstruction(tdzInit);
+ }
+
if (usesThis) {
Q_ASSERT(!isStrict);
// make sure we convert this to an object
@@ -295,13 +321,23 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
Q_ASSERT(nRegisters == 0);
registerOffset = bytecodeGenerator->currentRegister();
- if (contextType == ContextType::ESModule && !localNameForDefaultExport.isEmpty()) {
- if (!members.contains(localNameForDefaultExport)) {
- // allocate a local slot for the default export, to be used in
- // CodeGen::visit(ExportDeclaration*).
- locals.append(localNameForDefaultExport);
+ QVector<Context::MemberMap::Iterator> localsInTDZ;
+ const auto registerLocal = [this, &localsInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped()) {
+ localsInTDZ << member;
+ } else {
+ member->index = locals.size();
+ locals.append(member.key());
}
- }
+ };
+
+ QVector<Context::MemberMap::Iterator> registersInTDZ;
+ const auto allocateRegister = [bytecodeGenerator, &registersInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped())
+ registersInTDZ << member;
+ else
+ member->index = bytecodeGenerator->newRegister();
+ };
switch (contextType) {
case ContextType::ESModule:
@@ -309,15 +345,13 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
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);
+ registerLocal(it);
} else {
if (it->type == Context::ThisFunctionName)
it->index = CallData::Function;
else
- it->index = bytecodeGenerator->newRegister();
+ allocateRegister(it);
}
}
break;
@@ -327,15 +361,34 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
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();
- }
+ if (it->canEscape)
+ registerLocal(it);
+ else
+ allocateRegister(it);
}
break;
}
+
+ sizeOfLocalTemporalDeadZone = localsInTDZ.count();
+ for (auto &member: qAsConst(localsInTDZ)) {
+ member->index = locals.size();
+ locals.append(member.key());
+ }
+
+ if (contextType == ContextType::ESModule && !localNameForDefaultExport.isEmpty()) {
+ if (!members.contains(localNameForDefaultExport)) {
+ // allocate a local slot for the default export, to be used in
+ // CodeGen::visit(ExportDeclaration*).
+ locals.append(localNameForDefaultExport);
+ ++sizeOfLocalTemporalDeadZone;
+ }
+ }
+
+ sizeOfRegisterTemporalDeadZone = registersInTDZ.count();
+ firstTemporalDeadZoneRegister = bytecodeGenerator->currentRegister();
+ for (auto &member: qAsConst(registersInTDZ))
+ member->index = bytecodeGenerator->newRegister();
+
nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
}
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index bd0bd90a59..3df0aa6b3a 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -167,8 +167,10 @@ struct Context {
QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var;
mutable bool canEscape = false;
QQmlJS::AST::FunctionExpression *function = nullptr;
+ QQmlJS::AST::SourceLocation endOfInitializerLocation;
bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; }
+ bool requiresTDZCheck(const QQmlJS::AST::SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const;
};
typedef QMap<QString, Member> MemberMap;
@@ -189,6 +191,9 @@ struct Context {
int nRegisters = 0;
int registerOffset = -1;
+ int sizeOfLocalTemporalDeadZone = 0;
+ int firstTemporalDeadZoneRegister = 0;
+ int sizeOfRegisterTemporalDeadZone = 0;
bool hasDirectEval = false;
bool allVarsEscape = false;
bool hasNestedFunctions = false;
@@ -203,6 +208,7 @@ struct Context {
bool isWithBlock = false;
bool isCatchBlock = false;
QString caughtVariable;
+ QQmlJS::AST::SourceLocation lastBlockInitializerLocation;
enum UsesArgumentsObject {
ArgumentsObjectUnknown,
@@ -310,7 +316,8 @@ struct Context {
usedVariables.insert(name);
}
- bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr);
+ bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr,
+ const QQmlJS::AST::SourceLocation &endOfInitializer = QQmlJS::AST::SourceLocation());
struct ResolvedName {
enum Type {
@@ -323,11 +330,13 @@ struct Context {
Type type = Unresolved;
bool isArgOrEval = false;
bool isConst = false;
+ bool requiresTDZCheck = false;
int scope = -1;
int index = -1;
+ QQmlJS::AST::SourceLocation endOfDeclarationLocation;
bool isValid() const { return type != Unresolved; }
};
- ResolvedName resolveName(const QString &name);
+ ResolvedName resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation);
void emitBlockHeader(Compiler::Codegen *codegen);
void emitBlockFooter(Compiler::Codegen *codegen);
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 06335b3aa0..5d3a7a6d8c 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -312,6 +312,10 @@ bool ScanFunctions::visit(PatternElement *ast)
QStringList names;
ast->boundNames(&names);
+ QQmlJS::AST::SourceLocation lastInitializerLocation = ast->lastSourceLocation();
+ if (_context->lastBlockInitializerLocation.isValid())
+ lastInitializerLocation = _context->lastBlockInitializerLocation;
+
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"));
@@ -322,7 +326,8 @@ bool ScanFunctions::visit(PatternElement *ast)
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
return false;
}
- if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
+ if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope,
+ /*function*/nullptr, lastInitializerLocation)) {
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
return false;
}
@@ -485,6 +490,8 @@ void ScanFunctions::endVisit(ForStatement *)
bool ScanFunctions::visit(ForEachStatement *ast) {
enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach"));
+ if (ast->expression)
+ _context->lastBlockInitializerLocation = ast->expression->lastSourceLocation();
Node::accept(ast->lhs, this);
Node::accept(ast->expression, this);
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index d186c4e7e6..4653b7217d 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -156,7 +156,7 @@ QString dumpRegister(int reg, int nFormals)
return QStringLiteral("(this)");
else if (reg == CallData::Argc)
return QStringLiteral("(argc)");
- reg -= 4;
+ reg -= CallData::OffsetCount;
if (reg <= nFormals)
return QStringLiteral("a%1").arg(reg);
reg -= nFormals;
@@ -415,6 +415,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << "(" << level << ") " << ABSOLUTE_OFFSET();
MOTH_END_INSTR(UnwindToLabel)
+ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
+ d << name;
+ MOTH_END_INSTR(DeadTemporalZoneCheck)
+
MOTH_BEGIN_INSTR(ThrowException)
MOTH_END_INSTR(ThrowException)
@@ -690,6 +694,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_BEGIN_INSTR(Debug)
MOTH_END_INSTR(Debug)
+ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ d << dumpRegister(firstReg, nFormals) << ", " << count;
+ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
+
MOTH_BEGIN_INSTR(LoadQmlContext)
d << dumpRegister(result, nFormals);
MOTH_END_INSTR(LoadQmlContext)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index 25a07208c2..7c2cbf717e 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -118,6 +118,7 @@ QT_BEGIN_NAMESPACE
#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_DeadTemporalZoneCheck(op) INSTRUCTION(op, DeadTemporalZoneCheck, 1, name)
#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
@@ -192,6 +193,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs)
#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result)
#define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result)
+#define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count)
#define FOR_EACH_MOTH_INSTR_ALL(F) \
F(Nop) \
@@ -295,6 +297,7 @@ QT_BEGIN_NAMESPACE
F(SetUnwindHandler) \
F(UnwindDispatch) \
F(UnwindToLabel) \
+ F(DeadTemporalZoneCheck) \
F(ThrowException) \
F(GetException) \
F(SetException) \
@@ -326,6 +329,7 @@ QT_BEGIN_NAMESPACE
F(LoadSuperConstructor) \
F(PushScriptContext) \
F(PopScriptContext) \
+ F(InitializeBlockDeadTemporalZone) \
F(Debug) \
#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1)
diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index c7d8f3c72c..5950901499 100644
--- a/src/qml/jit/qv4baselineassembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -66,6 +66,7 @@ namespace JIT {
#define callHelper(x) PlatformAssemblerCommon::callRuntimeUnchecked(#x, reinterpret_cast<void *>(&x))
const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer;
+const QV4::Value::ValueTypeInternal EmptyTag = QV4::Value::ValueTypeInternal::Empty;
static ReturnedValue toNumberHelper(ReturnedValue v)
{
@@ -172,6 +173,11 @@ public:
return branch64(Equal, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue()));
}
+ Jump jumpNotEmpty()
+ {
+ return branch64(NotEqual, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue()));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
@@ -634,6 +640,11 @@ public:
return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32));
}
+ Jump jumpNotEmpty()
+ {
+ return branch32(NotEqual, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
@@ -1538,6 +1549,19 @@ void BaselineAssembler::popContext()
pasm()->storeHeapObject(PlatformAssembler::ScratchRegister, regAddr(CallData::Context));
}
+void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variableName)
+{
+ auto valueIsAliveJump = pasm()->jumpNotEmpty();
+ storeInstructionPointer(offsetForSavedIP);
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passInt32AsArg(variableName, 1);
+ passEngineAsArg(0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_throwReferenceError, CallResultDestination::Ignore);
+ gotoCatchException();
+ valueIsAliveJump.link(pasm());
+}
+
void BaselineAssembler::ret()
{
pasm()->generateFunctionExit();
diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h
index 4b64f91cd4..a2140ce47b 100644
--- a/src/qml/jit/qv4baselineassembler_p.h
+++ b/src/qml/jit/qv4baselineassembler_p.h
@@ -161,6 +161,7 @@ public:
void unwindToLabel(int level, int offset);
void pushCatchContext(int index, int name);
void popContext();
+ void deadTemporalZoneCheck(int offsetForSavedIP, int variableName);
// other stuff
void ret();
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index b729f21059..36375461f9 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -579,6 +579,10 @@ void BaselineJIT::generate_UnwindToLabel(int level, int offset)
as->unwindToLabel(level, absoluteOffsetForJump(offset));
}
+void BaselineJIT::generate_DeadTemporalZoneCheck(int name)
+{
+ as->deadTemporalZoneCheck(nextInstructionOffset(), name);
+}
void BaselineJIT::generate_ThrowException()
{
@@ -811,7 +815,7 @@ void BaselineJIT::generate_LoadSuperConstructor()
as->prepareCallWithArgCount(2);
as->passJSSlotAsArg(CallData::Function, 1);
as->passEngineAsArg(0);
- BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadSuperConstructor, CallResultDestination::InAccumulator);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperConstructor, CallResultDestination::InAccumulator);
as->checkException();
}
@@ -931,6 +935,13 @@ void BaselineJIT::generate_LoadQmlImportedScripts(int result)
as->storeReg(result);
}
+void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
+{
+ as->loadValue(Primitive::emptyValue().rawValue());
+ for (int i = firstReg, end = firstReg + count; i < end; ++i)
+ as->storeReg(i);
+}
+
void BaselineJIT::startInstruction(Instr::Type /*instr*/)
{
if (hasLabel())
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 07a1853816..a3cb850fc1 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -134,6 +134,7 @@ public:
void generate_SetUnwindHandler(int offset) override;
void generate_UnwindDispatch() override;
void generate_UnwindToLabel(int level, int offset) override;
+ void generate_DeadTemporalZoneCheck(int name) override;
void generate_ThrowException() override;
void generate_GetException() override;
void generate_SetException() override;
@@ -208,6 +209,7 @@ public:
void generate_Sub(int lhs) override;
void generate_LoadQmlContext(int result) override;
void generate_LoadQmlImportedScripts(int result) override;
+ void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override;
void startInstruction(Moth::Instr::Type instr) override;
void endInstruction(Moth::Instr::Type instr) override;
diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp
index 9e057dd33d..7bac5d968d 100644
--- a/src/qml/jit/qv4jithelpers.cpp
+++ b/src/qml/jit/qv4jithelpers.cpp
@@ -70,16 +70,6 @@ ReturnedValue loadGlobalLookup(ExecutionEngine *engine, Function *f, int index)
return l->globalGetter(l, engine);
}
-ReturnedValue loadSuperConstructor(ExecutionEngine *engine, const Value *t)
-{
- const FunctionObject *f = t->as<FunctionObject>();
- if (!f || !f->isConstructor()) {
- engine->throwTypeError();
- return Encode::undefined();
- }
- return static_cast<const Object *>(t)->getPrototypeOf()->asReturnedValue();
-}
-
ReturnedValue toObject(ExecutionEngine *engine, const Value &obj)
{
if (obj.isObject())
diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/jit/qv4jithelpers_p.h
index e0dfdc47d9..bb10d5722b 100644
--- a/src/qml/jit/qv4jithelpers_p.h
+++ b/src/qml/jit/qv4jithelpers_p.h
@@ -66,7 +66,6 @@ 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);
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index c162a38e9d..042d296276 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -49,6 +49,7 @@
#include "qv4string_p.h"
#include "qv4symbol_p.h"
#include <QtCore/qscopedvaluerollback.h>
+#include "qv4proxy_p.h"
using namespace QV4;
@@ -157,11 +158,12 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- bool isArray = argc && argv[0].as<ArrayObject>();
- return Encode(isArray);
+ if (!argc || !argv->objectValue())
+ return Encode(false);
+ return Encode(argv->objectValue()->isArray());
}
-ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len)
+static ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len)
{
ScopedObject a(scope, Primitive::undefinedValue());
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 327e7b78c4..d22179173c 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -76,6 +76,8 @@ Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int b
c->locals.size = nLocals;
c->locals.alloc = nLocals;
+ c->setupLocalTemporalDeadZone(function->compilationUnit->unitData()->blockAt(blockIndex));
+
return c;
}
@@ -115,6 +117,8 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
// memory allocated from the JS heap is 0 initialized, so check if empty is 0
Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0);
+ c->setupLocalTemporalDeadZone(compiledFunction);
+
Value *args = c->locals.values + nLocals;
::memcpy(args, frame->originalArguments, frame->originalArgumentsCount * sizeof(Value));
c->nArgs = frame->originalArgumentsCount;
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index fbb4168e9b..5de11d80cb 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -118,6 +118,12 @@ DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) {
return locals.data() + locals.size;
}
void setArg(uint index, Value v);
+
+ template <typename BlockOrFunction>
+ void setupLocalTemporalDeadZone(BlockOrFunction *bof) {
+ for (uint i = bof->nLocals - bof->sizeOfLocalTemporalDeadZone; i < bof->nLocals; ++i)
+ locals.values[i] = Primitive::emptyValue();
+ }
};
Q_STATIC_ASSERT(std::is_trivial< CallContext >::value);
Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value);
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index d6cfb1dcc1..c40d6414ff 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -485,10 +485,18 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
sequencePrototype()->cast<SequencePrototype>()->init();
#endif
+ jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(global);
+ jsObjects[WeakMapProto] = memoryManager->allocate<WeakMapPrototype>();
+ static_cast<WeakMapPrototype *>(weakMapPrototype())->init(this, weakMapCtor());
+
jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global);
jsObjects[MapProto] = memoryManager->allocate<MapPrototype>();
static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor());
+ jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(global);
+ jsObjects[WeakSetProto] = memoryManager->allocate<WeakSetPrototype>();
+ static_cast<WeakSetPrototype *>(weakSetPrototype())->init(this, weakSetCtor());
+
jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global);
jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
@@ -547,7 +555,9 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("WeakSet"), *weakSetCtor());
globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("WeakMap"), *weakMapCtor());
globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor());
for (int i = 0; i < NTypedArrayTypes; ++i)
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 0f4a4a75a2..0b5d8663eb 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -165,7 +165,9 @@ public:
SharedArrayBufferProto,
ArrayBufferProto,
DataViewProto,
+ WeakSetProto,
SetProto,
+ WeakMapProto,
MapProto,
IntrinsicTypedArrayProto,
ValueTypeProto,
@@ -197,7 +199,9 @@ public:
SharedArrayBuffer_Ctor,
ArrayBuffer_Ctor,
DataView_Ctor,
+ WeakSet_Ctor,
Set_Ctor,
+ WeakMap_Ctor,
Map_Ctor,
IntrinsicTypedArray_Ctor,
@@ -234,7 +238,9 @@ public:
FunctionObject *sharedArrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + SharedArrayBuffer_Ctor); }
FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); }
FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); }
+ FunctionObject *weakSetCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + WeakSet_Ctor); }
FunctionObject *setCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Set_Ctor); }
+ FunctionObject *weakMapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + WeakMap_Ctor); }
FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); }
FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); }
FunctionObject *typedArrayCtors;
@@ -268,7 +274,9 @@ public:
Object *sharedArrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + SharedArrayBufferProto); }
Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); }
Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); }
+ Object *weakSetPrototype() const { return reinterpret_cast<Object *>(jsObjects + WeakSetProto); }
Object *setPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetProto); }
+ Object *weakMapPrototype() const { return reinterpret_cast<Object *>(jsObjects + WeakMapProto); }
Object *mapPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapProto); }
Object *intrinsicTypedArrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + IntrinsicTypedArrayProto); }
Object *typedArrayPrototype;
diff --git a/src/qml/jsruntime/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp
index 55b7407000..aa39971dc6 100644
--- a/src/qml/jsruntime/qv4estable.cpp
+++ b/src/qml/jsruntime/qv4estable.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qv4estable_p.h"
+#include "qv4object_p.h"
using namespace QV4;
@@ -67,10 +68,11 @@ ESTable::~ESTable()
m_values = nullptr;
}
-void ESTable::markObjects(MarkStack *s)
+void ESTable::markObjects(MarkStack *s, bool isWeakMap)
{
for (uint i = 0; i < m_size; ++i) {
- m_keys[i].mark(s);
+ if (!isWeakMap)
+ m_keys[i].mark(s);
m_values[i].mark(s);
}
}
@@ -155,8 +157,8 @@ bool ESTable::remove(const Value &key)
}
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);
+ memmove(m_keys + idx, m_keys + idx + 1, (m_size - idx)*sizeof(Value));
+ memmove(m_values + idx, m_values + idx + 1, (m_size - idx)*sizeof(Value));
m_size--;
}
return found;
@@ -179,3 +181,19 @@ void ESTable::iterate(uint idx, Value *key, Value *value)
*value = m_values[idx];
}
+void ESTable::removeUnmarkedKeys()
+{
+ uint idx = 0;
+ uint toIdx = 0;
+ for (; idx < m_size; ++idx) {
+ Q_ASSERT(m_keys[idx].isObject());
+ Object &o = static_cast<Object &>(m_keys[idx]);
+ if (o.d()->isMarked()) {
+ m_keys[toIdx] = m_keys[idx];
+ m_values[toIdx] = m_values[idx];
+ ++toIdx;
+ }
+ }
+ m_size = toIdx;
+}
+
diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h
index c665467760..f54fc37a7b 100644
--- a/src/qml/jsruntime/qv4estable_p.h
+++ b/src/qml/jsruntime/qv4estable_p.h
@@ -64,7 +64,7 @@ public:
ESTable();
~ESTable();
- void markObjects(MarkStack *s);
+ void markObjects(MarkStack *s, bool isWeakMap);
void clear();
void set(const Value &k, const Value &v);
bool has(const Value &k) const;
@@ -73,6 +73,8 @@ public:
uint size() const;
void iterate(uint idx, Value *k, Value *v);
+ void removeUnmarkedKeys();
+
private:
Value *m_keys = nullptr;
Value *m_values = nullptr;
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index a5dc0ba567..f7bea9a5f0 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -556,7 +556,7 @@ ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject
CppStackFrame frame;
frame.init(v4, f->function(), argv, argc);
frame.setupJSFrame(v4->jsStackTop, *f, f->scope(),
- Primitive::undefinedValue(),
+ Primitive::emptyValue(),
newTarget ? *newTarget : Primitive::undefinedValue());
frame.push();
@@ -570,7 +570,7 @@ ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject
return Encode::undefined();
else if (Value::fromReturnedValue(result).isObject())
return result;
- else if (!Value::fromReturnedValue(result).isUndefined())
+ else if (!Value::fromReturnedValue(result).isUndefined() || frame.jsFrame->thisObject.isEmpty())
return v4->throwTypeError();
return frame.jsFrame->thisObject.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index e812894a97..f89a4b9909 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -195,6 +195,9 @@ namespace Heap {
struct DataView;
struct TypedArray;
+ struct MapObject;
+ struct SetObject;
+
template <typename T, size_t> struct Pointer;
}
@@ -242,6 +245,9 @@ struct ArrayBuffer;
struct DataView;
struct TypedArray;
+struct MapObject;
+struct SetMapObject;
+
// ReturnedValue is used to return values from runtime methods
// the type has to be a primitive type (no struct or union), so that the compiler
// will return it in a register on all platforms.
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index 55cedf50aa..31689b1ba1 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -86,6 +86,7 @@ struct JSCallData {
ptr->context = Encode::undefined();
ptr->accumulator = Encode::undefined();
ptr->thisObject = thisObject->asReturnedValue();
+ ptr->newTarget = Encode::undefined();
ptr->setArgc(argc);
if (argc)
memcpy(ptr->args, args, argc*sizeof(Value));
diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp
index ca9e1723f9..c598814fb6 100644
--- a/src/qml/jsruntime/qv4mapobject.cpp
+++ b/src/qml/jsruntime/qv4mapobject.cpp
@@ -45,65 +45,112 @@
using namespace QV4;
+DEFINE_OBJECT_VTABLE(WeakMapCtor);
DEFINE_OBJECT_VTABLE(MapCtor);
DEFINE_OBJECT_VTABLE(MapObject);
+void Heap::WeakMapCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("WeakMap"));
+}
+
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 *)
+ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakMap)
{
Scope scope(f);
Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>());
+ if (weakMap) {
+ a->setPrototypeOf(scope.engine->weakMapPrototype());
+ scope.engine->memoryManager->registerWeakMap(a->d());
+ }
+ a->d()->isWeakMap = weakMap;
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)) {
+ if (!iterable->isNullOrUndefined()) {
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();
+ ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+ if (scope.hasException())
+ return Encode::undefined();
+ Q_ASSERT(iter);
- Value *nextValue = scope.alloc(1);
+ ScopedValue obj(scope);
+ Value *arguments = scope.alloc(2);
ScopedValue done(scope);
forever {
- done = Runtime::method_iteratorNext(scope.engine, iter, nextValue);
- CHECK_EXCEPTION();
+ done = Runtime::method_iteratorNext(scope.engine, iter, obj);
+ if (scope.hasException())
+ break;
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();
+ const Object *o = obj->objectValue();
+ if (!o) {
+ scope.engine->throwTypeError();
+ break;
}
+
+ arguments[0] = o->get(PropertyKey::fromArrayIndex(0));
+ if (scope.hasException())
+ break;
+ arguments[1] = o->get(PropertyKey::fromArrayIndex(1));
+ if (scope.hasException())
+ break;
+
+ adder->call(a, arguments, 2);
+ if (scope.hasException())
+ break;
}
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iter, falsey);
}
}
- return a.asReturnedValue();
+ return a->asReturnedValue();
+
}
-ReturnedValue MapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+ReturnedValue WeakMapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, true);
+}
+
+ReturnedValue WeakMapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
{
Scope scope(f);
- return scope.engine->throwTypeError(QString::fromLatin1("Map requires new"));
+ return scope.engine->throwTypeError(QString::fromLatin1("(Weak)Map requires new"));
+}
+
+
+ReturnedValue MapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, false);
+}
+
+void WeakMapPrototype::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));
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("get"), method_get, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 2);
+
+ ScopedString val(scope, engine->newString(QLatin1String("WeakMap")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
}
+
void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
@@ -119,7 +166,7 @@ void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("get"), method_get, 1);
defineDefaultProperty(QStringLiteral("has"), method_has, 1);
defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
- defineDefaultProperty(QStringLiteral("set"), method_set, 0);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 2);
defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr);
defineDefaultProperty(QStringLiteral("values"), method_values, 0);
@@ -142,42 +189,97 @@ void Heap::MapObject::init()
void Heap::MapObject::destroy()
{
delete esTable;
- esTable = 0;
+ esTable = nullptr;
+}
+
+void Heap::MapObject::removeUnmarkedKeys()
+{
+ esTable->removeUnmarkedKeys();
}
void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack)
{
MapObject *m = static_cast<MapObject *>(that);
- m->esTable->markObjects(markStack);
+ m->esTable->markObjects(markStack, m->isWeakMap);
Object::markObjects(that, markStack);
}
+ReturnedValue WeakMapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->remove(argv[0]));
+
+}
+
+ReturnedValue WeakMapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode::undefined();
+
+ return that->d()->esTable->get(argv[0]);
+}
+
+ReturnedValue WeakMapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->has(argv[0]));
+}
+
+ReturnedValue WeakMapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if ((!that || !that->d()->isWeakMap) ||
+ (!argc || !argv[0].isObject()))
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argv[0], argc > 1 ? argv[1] : Primitive::undefinedValue());
+ return that.asReturnedValue();
+}
+
+
ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
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)
+ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
- return Encode(that->d()->esTable->remove(argv[0]));
+ return Encode(that->d()->esTable->remove(argc ? argv[0] : Primitive::undefinedValue()));
}
ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
@@ -189,7 +291,7 @@ ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
ScopedFunctionObject callbackfn(scope, argv[0]);
@@ -201,41 +303,41 @@ ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value
thisArg = ScopedValue(scope, argv[1]);
Value *arguments = scope.alloc(3);
+ arguments[2] = that;
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)
+ that->d()->esTable->iterate(i, &arguments[1], &arguments[0]); // 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)
+ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
- return that->d()->esTable->get(argv[0]);
+ return that->d()->esTable->get(argc ? argv[0] : Primitive::undefinedValue());
}
-ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
- return Encode(that->d()->esTable->has(argv[0]));
+ return Encode(that->d()->esTable->has(argc ? argv[0] : Primitive::undefinedValue()));
}
ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
@@ -243,14 +345,14 @@ ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *th
return ao->asReturnedValue();
}
-ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
- that->d()->esTable->set(argv[0], argv[1]);
+ that->d()->esTable->set(argc ? argv[0] : Primitive::undefinedValue(), argc > 1 ? argv[1] : Primitive::undefinedValue());
return that.asReturnedValue();
}
@@ -258,7 +360,7 @@ ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
return scope.engine->throwTypeError();
return Encode(that->d()->esTable->size());
@@ -268,12 +370,10 @@ ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *
{
Scope scope(b);
Scoped<MapObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakMap)
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
index 6793612bcb..a0fae2a14a 100644
--- a/src/qml/jsruntime/qv4mapobject_p.h
+++ b/src/qml/jsruntime/qv4mapobject_p.h
@@ -64,7 +64,11 @@ class ESTable;
namespace Heap {
-struct MapCtor : FunctionObject {
+struct WeakMapCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct MapCtor : WeakMapCtor {
void init(QV4::ExecutionContext *scope);
};
@@ -72,19 +76,32 @@ struct MapObject : Object {
static void markObjects(Heap::Base *that, MarkStack *markStack);
void init();
void destroy();
+ void removeUnmarkedKeys();
+
+ MapObject *nextWeakMap;
ESTable *esTable;
+ bool isWeakMap;
};
}
-struct MapCtor: FunctionObject
+struct WeakMapCtor: FunctionObject
{
- V4_OBJECT2(MapCtor, FunctionObject)
+ V4_OBJECT2(WeakMapCtor, FunctionObject)
+
+ static ReturnedValue construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakMap);
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 MapCtor : WeakMapCtor
+{
+ V4_OBJECT2(MapCtor, WeakMapCtor)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+};
+
struct MapObject : Object
{
V4_OBJECT2(MapObject, Object)
@@ -92,7 +109,17 @@ struct MapObject : Object
V4_NEEDS_DESTROY
};
-struct MapPrototype : Object
+struct WeakMapPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_delete(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_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapPrototype : WeakMapPrototype
{
void init(ExecutionEngine *engine, Object *ctor);
diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp
index 583d0c3756..2db229e2b0 100644
--- a/src/qml/jsruntime/qv4module.cpp
+++ b/src/qml/jsruntime/qv4module.cpp
@@ -70,6 +70,9 @@ void Heap::Module::init(ExecutionEngine *engine, CompiledData::CompilationUnit *
scope->locals.alloc = locals;
scope->nArgs = 0;
+ // Prepare the temporal dead zone
+ scope->setupLocalTemporalDeadZone(moduleFunction->compiledFunction);
+
Scope valueScope(engine);
// It's possible for example to re-export an import, for example:
@@ -108,6 +111,10 @@ ReturnedValue Module::virtualGet(const Managed *m, PropertyKey id, const Value *
*hasProperty = v != nullptr;
if (!v)
return Encode::undefined();
+ if (v->isEmpty()) {
+ ScopedValue propName(scope, id.toStringOrSymbol(scope.engine));
+ return scope.engine->throwReferenceError(propName);
+ }
return v->asReturnedValue();
}
@@ -126,10 +133,26 @@ PropertyAttributes Module::virtualGetOwnProperty(Managed *m, PropertyKey id, Pro
return Attr_Invalid;
}
if (p)
- p->value = v->asReturnedValue();
+ p->value = v->isEmpty() ? Encode::undefined() : v->asReturnedValue();
+ if (v->isEmpty()) {
+ ScopedValue propName(scope, id.toStringOrSymbol(scope.engine));
+ scope.engine->throwReferenceError(propName);
+ }
return Attr_Data | Attr_NotConfigurable;
}
+bool Module::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ if (id.isSymbol())
+ return Object::virtualHasProperty(m, id);
+
+ const Module *module = static_cast<const Module *>(m);
+ Scope scope(m->engine());
+ ScopedString expectedName(scope, id.toStringOrSymbol(scope.engine));
+ const Value *v = module->d()->unit->resolveExport(expectedName);
+ return v != nullptr;
+}
+
bool Module::virtualPreventExtensions(Managed *)
{
return true;
@@ -179,8 +202,13 @@ PropertyKey ModuleNamespaceIterator::next(const Object *o, Property *pd, Propert
Scope scope(module->engine());
ScopedString exportName(scope, scope.engine->newString(exportedNames.at(exportIndex)));
exportIndex++;
- if (pd)
- pd->value = *module->d()->unit->resolveExport(exportName);
+ const Value *v = module->d()->unit->resolveExport(exportName);
+ if (pd) {
+ if (v->isEmpty())
+ scope.engine->throwReferenceError(exportName);
+ else
+ pd->value = *v;
+ }
return exportName->toPropertyKey();
}
return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
diff --git a/src/qml/jsruntime/qv4module_p.h b/src/qml/jsruntime/qv4module_p.h
index bb004b3b44..073e0d1e54 100644
--- a/src/qml/jsruntime/qv4module_p.h
+++ b/src/qml/jsruntime/qv4module_p.h
@@ -77,6 +77,7 @@ struct Q_QML_EXPORT Module : public Object {
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static PropertyAttributes virtualGetOwnProperty(Managed *m, PropertyKey id, Property *p);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
static bool virtualPreventExtensions(Managed *);
static bool virtualDefineOwnProperty(Managed *, PropertyKey, const Property *, PropertyAttributes);
static bool virtualPut(Managed *, PropertyKey, const Value &, Value *);
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 3d193455e9..8158d8ddd9 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -115,8 +115,7 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString()
PropertyAttributes attrs;
Scope scope(engine);
- ScopedProperty p(scope);
- ScopedPropertyKey key(scope, next(p, &attrs));
+ ScopedPropertyKey key(scope, next(nullptr, &attrs));
if (!key->isValid())
return Encode::null();
@@ -163,12 +162,13 @@ PropertyKey ForInIteratorObject::nextProperty() const
Scope scope(this);
ScopedObject c(scope, d()->current);
ScopedObject o(scope);
+ ScopedProperty p(scope);
ScopedPropertyKey key(scope);
PropertyAttributes attrs;
while (1) {
while (1) {
- key = d()->iterator->next(c, nullptr, &attrs);
+ key = d()->iterator->next(c, p, &attrs);
if (!key->isValid())
break;
if (!attrs.isEnumerable() || key->isSymbol())
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index c968bff0fe..926d246373 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -563,8 +563,9 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value
ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
ScopedValue name(scope);
+ ScopedValue value(scope);
while (1) {
- name = it.nextPropertyNameAsString();
+ name = it.nextPropertyNameAsString(value);
if (name->isNull())
break;
a->push_back(name);
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
index 3dc0956e0c..766a0c4592 100644
--- a/src/qml/jsruntime/qv4reflect.cpp
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -200,9 +200,7 @@ ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const
if (scope.engine->hasException)
return false;
- bool hasProperty = false;
- (void) o->get(name, nullptr, &hasProperty);
- return Encode(hasProperty);
+ return Encode(o->hasProperty(name));
}
ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc)
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9180108a0c..3317003ea4 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -759,11 +759,15 @@ ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value
if (!o)
return engine->throwTypeError();
ScopedValue d(scope, o->get(engine->id_done()));
+ if (scope.hasException())
+ return Encode::undefined();
bool done = d->toBoolean();
if (done) {
*value = Encode::undefined();
} else {
*value = o->get(engine->id_value());
+ if (scope.hasException())
+ return Encode::undefined();
}
return Encode(done);
}
@@ -907,6 +911,22 @@ void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &pr
engine->throwTypeError();
}
+ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, const Value &t)
+{
+ if (engine->currentStackFrame->thisObject() != Primitive::emptyValue().asReturnedValue()) {
+ return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number
+ }
+ const FunctionObject *f = t.as<FunctionObject>();
+ if (!f)
+ return engine->throwTypeError();
+ Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf();
+ if (!c->vtable()->isFunctionObject || !static_cast<Heap::FunctionObject *>(c)->isConstructor())
+ return engine->throwTypeError();
+ return c->asReturnedValue();
+}
+
+
+
#endif // V4_BOOTSTRAP
uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
@@ -1364,7 +1384,6 @@ ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &fu
ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
{
- Q_UNIMPLEMENTED();
if (!function.isFunctionObject())
return engine->throwTypeError();
@@ -1470,6 +1489,12 @@ ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine)
return root;
}
+void Runtime::method_throwReferenceError(ExecutionEngine *engine, int nameIndex)
+{
+ Scope scope(engine);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ engine->throwReferenceError(name);
+}
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
{
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 3f65e6c6d6..5dafeee6ce 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -115,6 +115,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
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)) \
+ F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \
\
/* typeof */ \
F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \
@@ -132,6 +133,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \
F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \
F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \
+ F(void, throwReferenceError, (ExecutionEngine *engine, int nameIndex)) \
\
/* closures */ \
F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp
index 30e849bfed..65824926b9 100644
--- a/src/qml/jsruntime/qv4setobject.cpp
+++ b/src/qml/jsruntime/qv4setobject.cpp
@@ -46,17 +46,26 @@
using namespace QV4;
DEFINE_OBJECT_VTABLE(SetCtor);
+DEFINE_OBJECT_VTABLE(WeakSetCtor);
DEFINE_OBJECT_VTABLE(SetObject);
+void Heap::WeakSetCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("WeakSet"));
+}
+
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 *)
+ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool isWeak)
{
Scope scope(f);
Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>());
+ if (isWeak)
+ a->setPrototypeOf(scope.engine->weakSetPrototype());
+ a->d()->isWeakSet = isWeak;
if (argc > 0) {
ScopedValue iterable(scope, argv[0]);
@@ -89,12 +98,74 @@ ReturnedValue SetCtor::virtualCallAsConstructor(const FunctionObject *f, const V
return a.asReturnedValue();
}
-ReturnedValue SetCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+ReturnedValue WeakSetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, true);
+}
+
+ReturnedValue WeakSetCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
{
Scope scope(f);
return scope.engine->throwTypeError(QString::fromLatin1("Set requires new"));
}
+ReturnedValue SetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, false);
+}
+
+void WeakSetPrototype::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));
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("add"), method_add, 1);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+
+ ScopedString val(scope, engine->newString(QLatin1String("WeakSet")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+ReturnedValue WeakSetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if ((!that || !that->d()->isWeakSet) ||
+ (!argc || !argv[0].isObject()))
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argv[0], Primitive::undefinedValue());
+ return that.asReturnedValue();
+}
+
+ReturnedValue WeakSetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->remove(argv[0]));
+}
+
+ReturnedValue WeakSetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->has(argv[0]));
+}
+
void SetPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
@@ -136,10 +207,15 @@ void Heap::SetObject::destroy()
esTable = 0;
}
+void Heap::SetObject::removeUnmarkedKeys()
+{
+ esTable->removeUnmarkedKeys();
+}
+
void Heap::SetObject::markObjects(Heap::Base *that, MarkStack *markStack)
{
SetObject *s = static_cast<SetObject *>(that);
- s->esTable->markObjects(markStack);
+ s->esTable->markObjects(markStack, s->isWeakSet);
Object::markObjects(that, markStack);
}
@@ -147,7 +223,7 @@ ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thi
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
return scope.engine->throwTypeError();
that->d()->esTable->set(argv[0], Primitive::undefinedValue());
@@ -158,7 +234,7 @@ ReturnedValue SetPrototype::method_clear(const FunctionObject *b, const Value *t
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
return scope.engine->throwTypeError();
that->d()->esTable->clear();
@@ -169,7 +245,7 @@ ReturnedValue SetPrototype::method_delete(const FunctionObject *b, const Value *
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
return scope.engine->throwTypeError();
return Encode(that->d()->esTable->remove(argv[0]));
@@ -179,7 +255,7 @@ ReturnedValue SetPrototype::method_entries(const FunctionObject *b, const Value
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
return scope.engine->throwTypeError();
Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that));
@@ -191,7 +267,7 @@ ReturnedValue SetPrototype::method_forEach(const FunctionObject *b, const Value
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
return scope.engine->throwTypeError();
ScopedFunctionObject callbackfn(scope, argv[0]);
@@ -218,7 +294,7 @@ ReturnedValue SetPrototype::method_has(const FunctionObject *b, const Value *thi
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
return scope.engine->throwTypeError();
return Encode(that->d()->esTable->has(argv[0]));
@@ -228,7 +304,7 @@ ReturnedValue SetPrototype::method_get_size(const FunctionObject *b, const Value
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
return scope.engine->throwTypeError();
return Encode(that->d()->esTable->size());
@@ -238,11 +314,10 @@ ReturnedValue SetPrototype::method_values(const FunctionObject *b, const Value *
{
Scope scope(b);
Scoped<SetObject> that(scope, thisObject);
- if (!that)
+ if (!that || that->d()->isWeakSet)
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
index 34649c3f01..21584e2132 100644
--- a/src/qml/jsruntime/qv4setobject_p.h
+++ b/src/qml/jsruntime/qv4setobject_p.h
@@ -64,7 +64,12 @@ class ESTable;
namespace Heap {
-struct SetCtor : FunctionObject {
+struct WeakSetCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+
+struct SetCtor : WeakSetCtor {
void init(QV4::ExecutionContext *scope);
};
@@ -72,19 +77,33 @@ struct SetObject : Object {
static void markObjects(Heap::Base *that, MarkStack *markStack);
void init();
void destroy();
+ void removeUnmarkedKeys();
+
ESTable *esTable;
+ SetObject *nextWeakSet;
+ bool isWeakSet;
};
}
-struct SetCtor: FunctionObject
+
+struct WeakSetCtor: FunctionObject
{
- V4_OBJECT2(SetCtor, FunctionObject)
+ V4_OBJECT2(WeakSetCtor, FunctionObject)
+
+ static ReturnedValue construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakSet);
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 SetCtor : WeakSetCtor
+{
+ V4_OBJECT2(SetCtor, WeakSetCtor)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+};
+
struct SetObject : Object
{
V4_OBJECT2(SetObject, Object)
@@ -92,7 +111,17 @@ struct SetObject : Object
V4_NEEDS_DESTROY
};
-struct SetPrototype : Object
+struct WeakSetPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_add(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_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+struct SetPrototype : WeakSetPrototype
{
void init(ExecutionEngine *engine, Object *ctor);
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
index 878f9283e7..ca39a8f7bb 100644
--- a/src/qml/jsruntime/qv4stackframe_p.h
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -181,6 +181,15 @@ struct Q_QML_EXPORT CppStackFrame {
const Value *end = jsFrame->args + nRegisters;
for (Value *v = jsFrame->args + argc; v < end; ++v)
*v = Encode::undefined();
+
+ if (v4Function && v4Function->compiledFunction) {
+ const int firstDeadZoneRegister = v4Function->compiledFunction->firstTemporalDeadZoneRegister;
+ const int registerDeadZoneSize = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone;
+
+ const Value * tdzEnd = stackSpace + firstDeadZoneRegister + registerDeadZoneSize;
+ for (Value *v = stackSpace + firstDeadZoneRegister; v < tdzEnd; ++v)
+ *v = Primitive::emptyValue().asReturnedValue();
+ }
}
#endif
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index 7606af80cb..7492f8872d 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -467,6 +467,13 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val
return a->d()->type->read(a->d()->buffer->data->data() + byteOffset);
}
+bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ bool hasProperty = false;
+ virtualGet(m, id, nullptr, &hasProperty);
+ return hasProperty;
+}
+
bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
if (!id.isArrayIndex())
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index d29599f31e..909334adb0 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -166,6 +166,7 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object
using Object::get;
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
};
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index aed3fce6b1..fd2328beaa 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -784,6 +784,15 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
goto handleUnwind;
MOTH_END_INSTR(UnwindToLabel)
+ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
+ if (ACC.isEmpty()) {
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_throwReferenceError(engine, name);
+ goto handleUnwind;
+ }
+ MOTH_END_INSTR(DeadTemporalZoneCheck)
+
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
@@ -949,12 +958,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_END_INSTR(ConvertThisToObject)
MOTH_BEGIN_INSTR(LoadSuperConstructor)
- const FunctionObject *f = stack[CallData::Function].as<FunctionObject>();
- if (!f || !f->isConstructor()) {
- engine->throwTypeError();
- } else {
- acc = static_cast<const Object *>(f)->getPrototypeOf()->asReturnedValue();
- }
+ acc = Runtime::method_loadSuperConstructor(engine, stack[CallData::Function]);
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadSuperConstructor)
@@ -1322,6 +1326,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
return acc;
MOTH_END_INSTR(Ret)
+ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ acc = Encode(Primitive::emptyValue());
+ for (int i = firstReg, end = firstReg + count; i < end; ++i)
+ STACK_VALUE(i) = acc;
+ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
+
MOTH_BEGIN_INSTR(Debug)
#if QT_CONFIG(qml_debug)
STORE_IP();
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 258fac57af..b9e6327ea2 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -61,6 +61,8 @@
#include <algorithm>
#include "qv4alloca_p.h"
#include "qv4profiling_p.h"
+#include "qv4mapobject_p.h"
+#include "qv4setobject_p.h"
//#define MM_STATS
@@ -976,6 +978,29 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
(*it) = Primitive::undefinedValue();
}
+ // remove objects from weak maps and sets
+ Heap::MapObject *map = weakMaps;
+ Heap::MapObject **lastMap = &weakMaps;
+ while (map) {
+ if (map->isMarked()) {
+ map->removeUnmarkedKeys();
+ *lastMap = map;
+ lastMap = &map->nextWeakMap;
+ }
+ map = map->nextWeakMap;
+ }
+
+ Heap::SetObject *set = weakSets;
+ Heap::SetObject **lastSet = &weakSets;
+ while (set) {
+ if (set->isMarked()) {
+ set->removeUnmarkedKeys();
+ *lastSet = set;
+ lastSet = &set->nextWeakSet;
+ }
+ set = set->nextWeakSet;
+ }
+
// onDestruction handlers may have accessed other QObject wrappers and reset their value, so ensure
// that they are all set to undefined.
for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
@@ -1179,6 +1204,18 @@ size_t MemoryManager::getLargeItemsMem() const
return hugeItemAllocator.usedMem();
}
+void MemoryManager::registerWeakMap(Heap::MapObject *map)
+{
+ map->nextWeakMap = weakMaps;
+ weakMaps = map;
+}
+
+void MemoryManager::registerWeakSet(Heap::SetObject *set)
+{
+ set->nextWeakSet = weakSets;
+ weakSets = set;
+}
+
MemoryManager::~MemoryManager()
{
delete m_persistentValues;
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index e3a011caf9..bbbbb1aef6 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -274,6 +274,9 @@ public:
return static_cast<typename ManagedType::Data *>(b);
}
+ void registerWeakMap(Heap::MapObject *map);
+ void registerWeakSet(Heap::SetObject *set);
+
protected:
/// expects size to be aligned
Heap::Base *allocString(std::size_t unmanagedSize);
@@ -296,6 +299,8 @@ public:
PersistentValueStorage *m_persistentValues;
PersistentValueStorage *m_weakValues;
QVector<Value *> m_pendingFreedObjectWrapperValue;
+ Heap::MapObject *weakMaps = nullptr;
+ Heap::SetObject *weakSets = nullptr;
std::size_t unmanagedHeapSize = 0; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items.
std::size_t unmanagedHeapSizeGCLimit;
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index b303592e23..f9a405db6d 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -27,8 +27,6 @@ language/module-code/namespace/internals/set.js strictFails
# ----- test failures that should be fixed
built-ins/Array/from/proto-from-ctor-realm.js fails
-built-ins/Array/isArray/proxy-revoked.js fails
-built-ins/Array/isArray/proxy.js fails
built-ins/Array/length/define-own-prop-length-overflow-realm.js fails
built-ins/Array/of/proto-from-ctor-realm.js fails
built-ins/Array/proto-from-ctor-realm.js fails
@@ -205,36 +203,8 @@ built-ins/JSON/parse/revived-proxy.js fails
built-ins/JSON/stringify/replacer-proxy-revoked.js fails
built-ins/JSON/stringify/replacer-proxy.js fails
built-ins/JSON/stringify/value-proxy.js fails
-built-ins/Map/iterable-calls-set.js fails
-built-ins/Map/iterator-close-after-set-failure.js fails
-built-ins/Map/iterator-is-undefined-throws.js fails
-built-ins/Map/iterator-item-first-entry-returns-abrupt.js fails
-built-ins/Map/iterator-item-second-entry-returns-abrupt.js fails
-built-ins/Map/iterator-items-are-not-object-close-iterator.js fails
-built-ins/Map/iterator-items-are-not-object.js fails
-built-ins/Map/map-iterable-throws-when-set-is-not-callable.js fails
-built-ins/Map/map-iterable.js fails
built-ins/Map/proto-from-ctor-realm.js fails
-built-ins/Map/prototype/clear/context-is-weakmap-object-throws.js fails
-built-ins/Map/prototype/delete/context-is-weakmap-object-throws.js fails
-built-ins/Map/prototype/delete/does-not-break-iterators.js fails
-built-ins/Map/prototype/delete/returns-true-for-deleted-entry.js fails
-built-ins/Map/prototype/entries/does-not-have-mapdata-internal-slot-weakmap.js fails
-built-ins/Map/prototype/forEach/callback-parameters.js fails
-built-ins/Map/prototype/forEach/deleted-values-during-foreach.js fails
-built-ins/Map/prototype/forEach/does-not-have-mapdata-internal-slot-weakmap.js fails
-built-ins/Map/prototype/forEach/iterates-in-key-insertion-order.js fails
-built-ins/Map/prototype/forEach/iterates-values-added-after-foreach-begins.js fails
built-ins/Map/prototype/forEach/iterates-values-deleted-then-readded.js fails
-built-ins/Map/prototype/get/does-not-have-mapdata-internal-slot-weakmap.js fails
-built-ins/Map/prototype/has/does-not-have-mapdata-internal-slot-weakmap.js fails
-built-ins/Map/prototype/keys/does-not-have-mapdata-internal-slot-weakmap.js fails
-built-ins/Map/prototype/set/append-new-values.js fails
-built-ins/Map/prototype/set/does-not-have-mapdata-internal-slot-weakmap.js fails
-built-ins/Map/prototype/set/length.js fails
-built-ins/Map/prototype/size/does-not-have-mapdata-internal-slot-weakmap.js fails
-built-ins/Map/prototype/size/returns-count-of-present-values-by-iterable.js fails
-built-ins/Map/prototype/values/does-not-have-mapdata-internal-slot-weakmap.js fails
built-ins/Math/max/zeros.js fails
built-ins/Math/round/S15.8.2.15_A7.js fails
built-ins/Number/isFinite/arg-is-not-number.js fails
@@ -489,15 +459,8 @@ built-ins/RegExp/unicode_restricted_octal_escape.js fails
built-ins/RegExp/unicode_restricted_quantifiable_assertion.js fails
built-ins/RegExp/u180e.js fails
built-ins/Set/proto-from-ctor-realm.js fails
-built-ins/Set/prototype/add/does-not-have-setdata-internal-slot-weakset.js fails
-built-ins/Set/prototype/clear/does-not-have-setdata-internal-slot-weakset.js fails
-built-ins/Set/prototype/delete/does-not-have-setdata-internal-slot-weakset.js fails
-built-ins/Set/prototype/entries/does-not-have-setdata-internal-slot-weakset.js fails
-built-ins/Set/prototype/forEach/does-not-have-setdata-internal-slot-weakset.js fails
built-ins/Set/prototype/forEach/iterates-values-revisits-after-delete-re-add.js fails
built-ins/Set/prototype/forEach/this-arg-explicit-cannot-override-lexical-this-arrow.js fails
-built-ins/Set/prototype/has/does-not-have-setdata-internal-slot-weakset.js fails
-built-ins/Set/prototype/values/does-not-have-setdata-internal-slot-weakset.js fails
built-ins/SharedArrayBuffer/data-allocation-after-object-creation.js fails
built-ins/SharedArrayBuffer/proto-from-ctor-realm.js fails
built-ins/SharedArrayBuffer/prototype-from-newtarget.js fails
@@ -657,169 +620,8 @@ built-ins/TypedArrays/internals/Set/key-is-minus-zero.js fails
built-ins/TypedArrays/internals/Set/key-is-not-integer.js fails
built-ins/TypedArrays/internals/Set/key-is-out-of-bounds.js fails
built-ins/TypedArrays/internals/Set/tonumber-value-throws.js strictFails
-built-ins/WeakMap/constructor.js fails
-built-ins/WeakMap/empty-iterable.js fails
-built-ins/WeakMap/get-set-method-failure.js fails
-built-ins/WeakMap/iterable-failure.js fails
-built-ins/WeakMap/iterable.js fails
-built-ins/WeakMap/iterator-close-after-set-failure.js fails
-built-ins/WeakMap/iterator-item-first-entry-returns-abrupt.js fails
-built-ins/WeakMap/iterator-item-second-entry-returns-abrupt.js fails
-built-ins/WeakMap/iterator-items-are-not-object-close-iterator.js fails
-built-ins/WeakMap/iterator-items-are-not-object.js fails
-built-ins/WeakMap/iterator-next-failure.js fails
-built-ins/WeakMap/iterator-value-failure.js fails
-built-ins/WeakMap/length.js fails
-built-ins/WeakMap/name.js fails
-built-ins/WeakMap/no-iterable.js fails
-built-ins/WeakMap/properties-of-map-instances.js fails
-built-ins/WeakMap/properties-of-the-weakmap-prototype-object.js fails
built-ins/WeakMap/proto-from-ctor-realm.js fails
-built-ins/WeakMap/prototype-of-weakmap.js fails
-built-ins/WeakMap/prototype/Symbol.toStringTag.js fails
-built-ins/WeakMap/prototype/constructor.js fails
-built-ins/WeakMap/prototype/delete/delete-entry-initial-iterable.js fails
-built-ins/WeakMap/prototype/delete/delete-entry.js fails
-built-ins/WeakMap/prototype/delete/delete.js fails
-built-ins/WeakMap/prototype/delete/does-not-have-weakmapdata-internal-slot-array.js fails
-built-ins/WeakMap/prototype/delete/does-not-have-weakmapdata-internal-slot-map.js fails
-built-ins/WeakMap/prototype/delete/does-not-have-weakmapdata-internal-slot-object.js fails
-built-ins/WeakMap/prototype/delete/does-not-have-weakmapdata-internal-slot-set.js fails
-built-ins/WeakMap/prototype/delete/does-not-have-weakmapdata-internal-slot-weakmap-prototype.js fails
-built-ins/WeakMap/prototype/delete/length.js fails
-built-ins/WeakMap/prototype/delete/name.js fails
-built-ins/WeakMap/prototype/delete/returns-false-value-is-not-object.js fails
-built-ins/WeakMap/prototype/delete/returns-false-when-delete-is-noop.js fails
-built-ins/WeakMap/prototype/delete/this-not-object-throw-boolean.js fails
-built-ins/WeakMap/prototype/delete/this-not-object-throw-null.js fails
-built-ins/WeakMap/prototype/delete/this-not-object-throw-number.js fails
-built-ins/WeakMap/prototype/delete/this-not-object-throw-string.js fails
-built-ins/WeakMap/prototype/delete/this-not-object-throw-symbol.js fails
-built-ins/WeakMap/prototype/delete/this-not-object-throw-undefined.js fails
-built-ins/WeakMap/prototype/get/does-not-have-weakmapdata-internal-slot-map.js fails
-built-ins/WeakMap/prototype/get/does-not-have-weakmapdata-internal-slot-set.js fails
-built-ins/WeakMap/prototype/get/does-not-have-weakmapdata-internal-slot.js fails
-built-ins/WeakMap/prototype/get/get.js fails
-built-ins/WeakMap/prototype/get/length.js fails
-built-ins/WeakMap/prototype/get/name.js fails
-built-ins/WeakMap/prototype/get/returns-undefined-key-is-not-object.js fails
-built-ins/WeakMap/prototype/get/returns-undefined.js fails
-built-ins/WeakMap/prototype/get/returns-value.js fails
-built-ins/WeakMap/prototype/get/this-not-object-throw.js fails
-built-ins/WeakMap/prototype/has/does-not-have-weakmapdata-internal-slot-array.js fails
-built-ins/WeakMap/prototype/has/does-not-have-weakmapdata-internal-slot-map.js fails
-built-ins/WeakMap/prototype/has/does-not-have-weakmapdata-internal-slot-object.js fails
-built-ins/WeakMap/prototype/has/does-not-have-weakmapdata-internal-slot-set.js fails
-built-ins/WeakMap/prototype/has/does-not-have-weakmapdata-internal-slot-weakmap-prototype.js fails
-built-ins/WeakMap/prototype/has/has.js fails
-built-ins/WeakMap/prototype/has/length.js fails
-built-ins/WeakMap/prototype/has/name.js fails
-built-ins/WeakMap/prototype/has/returns-false-when-value-is-not-object.js fails
-built-ins/WeakMap/prototype/has/returns-false-when-value-not-present.js fails
-built-ins/WeakMap/prototype/has/returns-true-when-value-present.js fails
-built-ins/WeakMap/prototype/has/this-not-object-throw-boolean.js fails
-built-ins/WeakMap/prototype/has/this-not-object-throw-null.js fails
-built-ins/WeakMap/prototype/has/this-not-object-throw-number.js fails
-built-ins/WeakMap/prototype/has/this-not-object-throw-string.js fails
-built-ins/WeakMap/prototype/has/this-not-object-throw-symbol.js fails
-built-ins/WeakMap/prototype/has/this-not-object-throw-undefined.js fails
-built-ins/WeakMap/prototype/prototype-attributes.js fails
-built-ins/WeakMap/prototype/set/adds-element.js fails
-built-ins/WeakMap/prototype/set/does-not-have-weakmapdata-internal-slot-array.js fails
-built-ins/WeakMap/prototype/set/does-not-have-weakmapdata-internal-slot-map.js fails
-built-ins/WeakMap/prototype/set/does-not-have-weakmapdata-internal-slot-object.js fails
-built-ins/WeakMap/prototype/set/does-not-have-weakmapdata-internal-slot-set.js fails
-built-ins/WeakMap/prototype/set/does-not-have-weakmapdata-internal-slot-weakmap-prototype.js fails
-built-ins/WeakMap/prototype/set/key-not-object-throw.js fails
-built-ins/WeakMap/prototype/set/length.js fails
-built-ins/WeakMap/prototype/set/name.js fails
-built-ins/WeakMap/prototype/set/returns-this-when-ignoring-duplicate.js fails
-built-ins/WeakMap/prototype/set/returns-this.js fails
-built-ins/WeakMap/prototype/set/set.js fails
-built-ins/WeakMap/prototype/set/this-not-object-throw-boolean.js fails
-built-ins/WeakMap/prototype/set/this-not-object-throw-null.js fails
-built-ins/WeakMap/prototype/set/this-not-object-throw-number.js fails
-built-ins/WeakMap/prototype/set/this-not-object-throw-string.js fails
-built-ins/WeakMap/prototype/set/this-not-object-throw-symbol.js fails
-built-ins/WeakMap/prototype/set/this-not-object-throw-undefined.js fails
-built-ins/WeakMap/set-not-callable-throws.js fails
-built-ins/WeakMap/undefined-newtarget.js fails
-built-ins/WeakMap/weakmap.js fails
-built-ins/WeakSet/add-not-callable-throws.js fails
-built-ins/WeakSet/constructor.js fails
-built-ins/WeakSet/empty-iterable.js fails
-built-ins/WeakSet/get-add-method-failure.js fails
-built-ins/WeakSet/iterable-failure.js fails
-built-ins/WeakSet/iterable.js fails
-built-ins/WeakSet/iterator-close-after-add-failure.js fails
-built-ins/WeakSet/iterator-next-failure.js fails
-built-ins/WeakSet/iterator-value-failure.js fails
-built-ins/WeakSet/length.js fails
-built-ins/WeakSet/name.js fails
-built-ins/WeakSet/no-iterable.js fails
-built-ins/WeakSet/properties-of-the-weakset-prototype-object.js fails
built-ins/WeakSet/proto-from-ctor-realm.js fails
-built-ins/WeakSet/prototype-of-weakset.js fails
-built-ins/WeakSet/prototype/Symbol.toStringTag.js fails
-built-ins/WeakSet/prototype/add/add.js fails
-built-ins/WeakSet/prototype/add/adds-element.js fails
-built-ins/WeakSet/prototype/add/does-not-have-weaksetdata-internal-slot-array.js fails
-built-ins/WeakSet/prototype/add/does-not-have-weaksetdata-internal-slot-map.js fails
-built-ins/WeakSet/prototype/add/does-not-have-weaksetdata-internal-slot-object.js fails
-built-ins/WeakSet/prototype/add/does-not-have-weaksetdata-internal-slot-set.js fails
-built-ins/WeakSet/prototype/add/does-not-have-weaksetdata-internal-slot-weakset-prototype.js fails
-built-ins/WeakSet/prototype/add/length.js fails
-built-ins/WeakSet/prototype/add/name.js fails
-built-ins/WeakSet/prototype/add/returns-this-when-ignoring-duplicate.js fails
-built-ins/WeakSet/prototype/add/returns-this.js fails
-built-ins/WeakSet/prototype/add/this-not-object-throw-boolean.js fails
-built-ins/WeakSet/prototype/add/this-not-object-throw-null.js fails
-built-ins/WeakSet/prototype/add/this-not-object-throw-number.js fails
-built-ins/WeakSet/prototype/add/this-not-object-throw-string.js fails
-built-ins/WeakSet/prototype/add/this-not-object-throw-symbol.js fails
-built-ins/WeakSet/prototype/add/this-not-object-throw-undefined.js fails
-built-ins/WeakSet/prototype/add/value-not-object-throw.js fails
-built-ins/WeakSet/prototype/constructor/weakset-prototype-constructor-intrinsic.js fails
-built-ins/WeakSet/prototype/constructor/weakset-prototype-constructor.js fails
-built-ins/WeakSet/prototype/delete/delete-entry-initial-iterable.js fails
-built-ins/WeakSet/prototype/delete/delete-entry.js fails
-built-ins/WeakSet/prototype/delete/delete.js fails
-built-ins/WeakSet/prototype/delete/does-not-have-weaksetdata-internal-slot-array.js fails
-built-ins/WeakSet/prototype/delete/does-not-have-weaksetdata-internal-slot-map.js fails
-built-ins/WeakSet/prototype/delete/does-not-have-weaksetdata-internal-slot-object.js fails
-built-ins/WeakSet/prototype/delete/does-not-have-weaksetdata-internal-slot-set.js fails
-built-ins/WeakSet/prototype/delete/does-not-have-weaksetdata-internal-slot-weakset-prototype.js fails
-built-ins/WeakSet/prototype/delete/length.js fails
-built-ins/WeakSet/prototype/delete/name.js fails
-built-ins/WeakSet/prototype/delete/returns-false-value-is-not-object.js fails
-built-ins/WeakSet/prototype/delete/returns-false-when-delete-is-noop.js fails
-built-ins/WeakSet/prototype/delete/this-not-object-throw-boolean.js fails
-built-ins/WeakSet/prototype/delete/this-not-object-throw-null.js fails
-built-ins/WeakSet/prototype/delete/this-not-object-throw-number.js fails
-built-ins/WeakSet/prototype/delete/this-not-object-throw-string.js fails
-built-ins/WeakSet/prototype/delete/this-not-object-throw-symbol.js fails
-built-ins/WeakSet/prototype/delete/this-not-object-throw-undefined.js fails
-built-ins/WeakSet/prototype/has/does-not-have-weaksetdata-internal-slot-array.js fails
-built-ins/WeakSet/prototype/has/does-not-have-weaksetdata-internal-slot-map.js fails
-built-ins/WeakSet/prototype/has/does-not-have-weaksetdata-internal-slot-object.js fails
-built-ins/WeakSet/prototype/has/does-not-have-weaksetdata-internal-slot-set.js fails
-built-ins/WeakSet/prototype/has/does-not-have-weaksetdata-internal-slot-weakset-prototype.js fails
-built-ins/WeakSet/prototype/has/has.js fails
-built-ins/WeakSet/prototype/has/length.js fails
-built-ins/WeakSet/prototype/has/name.js fails
-built-ins/WeakSet/prototype/has/returns-false-when-value-is-not-object.js fails
-built-ins/WeakSet/prototype/has/returns-false-when-value-not-present.js fails
-built-ins/WeakSet/prototype/has/returns-true-when-value-present.js fails
-built-ins/WeakSet/prototype/has/this-not-object-throw-boolean.js fails
-built-ins/WeakSet/prototype/has/this-not-object-throw-null.js fails
-built-ins/WeakSet/prototype/has/this-not-object-throw-number.js fails
-built-ins/WeakSet/prototype/has/this-not-object-throw-string.js fails
-built-ins/WeakSet/prototype/has/this-not-object-throw-symbol.js fails
-built-ins/WeakSet/prototype/has/this-not-object-throw-undefined.js fails
-built-ins/WeakSet/prototype/prototype-attributes.js fails
-built-ins/WeakSet/symbol-disallowed-as-weakset-key.js fails
-built-ins/WeakSet/undefined-newtarget.js fails
-built-ins/WeakSet/weakset.js fails
built-ins/global/global-object.js fails
built-ins/global/property-descriptor.js fails
built-ins/isFinite/toprimitive-not-callable-throws.js fails
@@ -827,9 +629,6 @@ built-ins/isNaN/toprimitive-not-callable-throws.js fails
language/computed-property-names/class/static/method-number.js fails
language/computed-property-names/class/static/method-string.js fails
language/computed-property-names/class/static/method-symbol.js fails
-language/eval-code/direct/lex-env-no-init-cls.js fails
-language/eval-code/direct/lex-env-no-init-const.js fails
-language/eval-code/direct/lex-env-no-init-let.js fails
language/eval-code/direct/new.target.js fails
language/eval-code/direct/new.target-arrow.js fails
language/eval-code/direct/new.target-fn.js fails
@@ -852,9 +651,6 @@ language/eval-code/direct/var-env-global-lex-non-strict.js sloppyFails
language/eval-code/direct/var-env-lower-lex-catch-non-strict.js sloppyFails
language/eval-code/direct/var-env-lower-lex-non-strict.js sloppyFails
language/eval-code/indirect/always-non-strict.js strictFails
-language/eval-code/indirect/lex-env-no-init-cls.js fails
-language/eval-code/indirect/lex-env-no-init-const.js fails
-language/eval-code/indirect/lex-env-no-init-let.js fails
language/eval-code/indirect/new.target.js fails
language/eval-code/indirect/non-definable-function-with-function.js sloppyFails
language/eval-code/indirect/non-definable-function-with-variable.js sloppyFails
@@ -904,7 +700,6 @@ language/expressions/assignment/S11.13.1_A7_T2.js fails
language/expressions/assignment/S11.13.1_A7_T3.js fails
language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order.js fails
language/expressions/assignment/destructuring/keyed-destructuring-property-reference-target-evaluation-order.js fails
-language/expressions/assignment/dstr-array-elem-init-let.js fails
language/expressions/assignment/dstr-array-elem-iter-rtrn-close-err.js fails
language/expressions/assignment/dstr-array-elem-iter-rtrn-close-null.js fails
language/expressions/assignment/dstr-array-elem-iter-rtrn-close.js fails
@@ -930,9 +725,7 @@ language/expressions/assignment/dstr-array-rest-lref-err.js fails
language/expressions/assignment/dstr-array-rest-put-let.js fails
language/expressions/assignment/dstr-obj-empty-null.js fails
language/expressions/assignment/dstr-obj-empty-undef.js fails
-language/expressions/assignment/dstr-obj-id-init-let.js fails
language/expressions/assignment/dstr-obj-id-put-let.js fails
-language/expressions/assignment/dstr-obj-prop-elem-init-let.js fails
language/expressions/assignment/dstr-obj-prop-put-let.js fails
language/expressions/assignment/fn-name-lhs-cover.js fails
language/expressions/assignment/fn-name-lhs-member.js fails
@@ -1092,8 +885,6 @@ language/expressions/generators/yield-identifier-non-strict.js sloppyFails
language/expressions/generators/yield-star-before-newline.js fails
language/expressions/logical-and/tco-right.js strictFails
language/expressions/logical-or/tco-right.js strictFails
-language/expressions/new.target/value-via-super-call.js fails
-language/expressions/new.target/value-via-super-property.js fails
language/expressions/new/non-ctor-err-realm.js fails
language/expressions/object/let-non-strict-access.js sloppyFails
language/expressions/object/let-non-strict-syntax.js sloppyFails
@@ -1150,27 +941,18 @@ language/expressions/prefix-increment/S11.4.4_A5_T3.js sloppyFails
language/expressions/prefix-increment/S11.4.4_A5_T4.js sloppyFails
language/expressions/prefix-increment/S11.4.4_A5_T5.js fails
language/expressions/prefix-increment/S11.4.4_A6_T3.js fails
-language/expressions/super/call-bind-this-value-twice.js fails
-language/expressions/super/call-construct-invocation.js fails
-language/expressions/super/call-proto-not-ctor.js fails
-language/expressions/super/prop-dot-cls-null-proto.js fails
language/expressions/super/prop-dot-cls-ref-strict.js fails
language/expressions/super/prop-dot-cls-ref-this.js fails
language/expressions/super/prop-dot-cls-this-uninit.js fails
language/expressions/super/prop-dot-cls-val-from-arrow.js fails
-language/expressions/super/prop-dot-obj-null-proto.js fails
language/expressions/super/prop-dot-obj-ref-non-strict.js sloppyFails
language/expressions/super/prop-dot-obj-ref-strict.js strictFails
language/expressions/super/prop-dot-obj-ref-this.js fails
language/expressions/super/prop-dot-obj-val-from-arrow.js fails
-language/expressions/super/prop-expr-cls-key-err.js fails
-language/expressions/super/prop-expr-cls-null-proto.js fails
language/expressions/super/prop-expr-cls-ref-strict.js fails
language/expressions/super/prop-expr-cls-ref-this.js fails
language/expressions/super/prop-expr-cls-this-uninit.js fails
language/expressions/super/prop-expr-cls-val-from-arrow.js fails
-language/expressions/super/prop-expr-obj-key-err.js fails
-language/expressions/super/prop-expr-obj-null-proto.js fails
language/expressions/super/prop-expr-obj-ref-non-strict.js sloppyFails
language/expressions/super/prop-expr-obj-ref-strict.js strictFails
language/expressions/super/prop-expr-obj-ref-this.js fails
@@ -1256,29 +1038,6 @@ language/global-code/script-decl-var.js fails
language/identifiers/other_id_continue.js fails
language/identifiers/other_id_start-escaped.js fails
language/identifiers/other_id_start.js fails
-language/module-code/instn-iee-bndng-cls.js strictFails
-language/module-code/instn-iee-bndng-const.js strictFails
-language/module-code/instn-iee-bndng-let.js strictFails
-language/module-code/instn-local-bndng-cls.js strictFails
-language/module-code/instn-local-bndng-const.js strictFails
-language/module-code/instn-local-bndng-export-cls.js strictFails
-language/module-code/instn-local-bndng-export-const.js strictFails
-language/module-code/instn-local-bndng-export-let.js strictFails
-language/module-code/instn-local-bndng-let.js strictFails
-language/module-code/instn-named-bndng-cls.js strictFails
-language/module-code/instn-named-bndng-const.js strictFails
-language/module-code/instn-named-bndng-dflt-cls.js strictFails
-language/module-code/instn-named-bndng-dflt-expr.js strictFails
-language/module-code/instn-named-bndng-dflt-named.js strictFails
-language/module-code/instn-named-bndng-dflt-star.js strictFails
-language/module-code/instn-named-bndng-let.js strictFails
-language/module-code/namespace/internals/delete-exported-uninit.js strictFails
-language/module-code/namespace/internals/enumerate-binding-uninit.js strictFails
-language/module-code/namespace/internals/get-own-property-str-found-uninit.js strictFails
-language/module-code/namespace/internals/get-str-found-uninit.js strictFails
-language/module-code/namespace/internals/object-hasOwnProperty-binding-uninit.js strictFails
-language/module-code/namespace/internals/object-keys-binding-uninit.js strictFails
-language/module-code/namespace/internals/object-propertyIsEnumerable-binding-uninit.js strictFails
language/statements/async-function/cptn-decl.js fails
language/statements/async-function/declaration-returns-promise.js fails
language/statements/async-function/evaluation-body.js fails
@@ -1301,7 +1060,6 @@ language/statements/class/definition/numeric-property-names.js fails
language/statements/class/definition/prototype-getter.js fails
language/statements/class/definition/prototype-property.js fails
language/statements/class/definition/prototype-setter.js fails
-language/statements/class/definition/prototype-wiring.js fails
language/statements/class/definition/setters-prop-desc.js fails
language/statements/class/definition/setters-restricted-ids.js fails
language/statements/class/definition/this-access-restriction-2.js fails
@@ -1315,9 +1073,6 @@ language/statements/class/meth-dflt-params-ref-later.js fails
language/statements/class/meth-dflt-params-ref-self.js fails
language/statements/class/meth-static-dflt-params-ref-later.js fails
language/statements/class/meth-static-dflt-params-ref-self.js fails
-language/statements/class/name-binding/in-extends-expression-assigned.js fails
-language/statements/class/name-binding/in-extends-expression-grouped.js fails
-language/statements/class/name-binding/in-extends-expression.js fails
language/statements/class/scope-gen-meth-paramsbody-var-open.js fails
language/statements/class/scope-meth-paramsbody-var-open.js fails
language/statements/class/scope-setter-paramsbody-var-open.js fails
@@ -1334,7 +1089,6 @@ language/statements/class/subclass/builtin-objects/Date/super-must-be-called.js
language/statements/class/subclass/builtin-objects/Error/super-must-be-called.js fails
language/statements/class/subclass/builtin-objects/Function/super-must-be-called.js fails
language/statements/class/subclass/builtin-objects/GeneratorFunction/super-must-be-called.js fails
-language/statements/class/subclass/builtin-objects/Map/regular-subclassing.js fails
language/statements/class/subclass/builtin-objects/Map/super-must-be-called.js fails
language/statements/class/subclass/builtin-objects/NativeError/EvalError-super.js fails
language/statements/class/subclass/builtin-objects/NativeError/RangeError-super.js fails
@@ -1350,36 +1104,18 @@ language/statements/class/subclass/builtin-objects/RegExp/super-must-be-called.j
language/statements/class/subclass/builtin-objects/Set/super-must-be-called.js fails
language/statements/class/subclass/builtin-objects/String/super-must-be-called.js fails
language/statements/class/subclass/builtin-objects/TypedArray/super-must-be-called.js fails
-language/statements/class/subclass/builtin-objects/WeakMap/regular-subclassing.js fails
language/statements/class/subclass/builtin-objects/WeakMap/super-must-be-called.js fails
-language/statements/class/subclass/builtin-objects/WeakSet/regular-subclassing.js fails
language/statements/class/subclass/builtin-objects/WeakSet/super-must-be-called.js fails
language/statements/class/subclass/builtins.js fails
-language/statements/class/subclass/class-definition-null-proto-super.js fails
language/statements/class/subclass/class-definition-null-proto-this.js fails
language/statements/class/subclass/default-constructor-spread-override.js fails
language/statements/class/super/in-methods.js fails
-language/statements/const/block-local-closure-get-before-initialization.js fails
-language/statements/const/block-local-use-before-initialization-in-declaration-statement.js fails
-language/statements/const/block-local-use-before-initialization-in-prior-statement.js fails
-language/statements/const/function-local-closure-get-before-initialization.js fails
-language/statements/const/function-local-use-before-initialization-in-declaration-statement.js fails
-language/statements/const/function-local-use-before-initialization-in-prior-statement.js fails
-language/statements/const/global-closure-get-before-initialization.js fails
-language/statements/const/global-use-before-initialization-in-declaration-statement.js fails
-language/statements/const/global-use-before-initialization-in-prior-statement.js fails
language/statements/do-while/tco-body.js strictFails
-language/statements/for-in/head-const-bound-names-fordecl-tdz.js fails
-language/statements/for-in/head-let-bound-names-fordecl-tdz.js fails
language/statements/for-in/head-lhs-let.js sloppyFails
language/statements/for-in/head-var-bound-names-let.js sloppyFails
language/statements/for-in/identifier-let-allowed-as-lefthandside-expression-not-strict.js sloppyFails
-language/statements/for-in/scope-body-lex-open.js fails
-language/statements/for-in/scope-head-lex-close.js fails
-language/statements/for-in/scope-head-lex-open.js fails
language/statements/for-of/body-dstr-assign-error.js fails
language/statements/for-of/body-put-error.js fails
-language/statements/for-of/dstr-array-elem-init-let.js fails
language/statements/for-of/dstr-array-elem-iter-rtrn-close-err.js fails
language/statements/for-of/dstr-array-elem-iter-rtrn-close-null.js fails
language/statements/for-of/dstr-array-elem-iter-rtrn-close.js fails
@@ -1405,23 +1141,16 @@ language/statements/for-of/dstr-array-rest-lref-err.js fails
language/statements/for-of/dstr-array-rest-put-let.js fails
language/statements/for-of/dstr-obj-empty-null.js fails
language/statements/for-of/dstr-obj-empty-undef.js fails
-language/statements/for-of/dstr-obj-id-init-let.js fails
language/statements/for-of/dstr-obj-id-put-let.js fails
-language/statements/for-of/dstr-obj-prop-elem-init-let.js fails
language/statements/for-of/dstr-obj-prop-put-let.js fails
language/statements/for-of/generator-close-via-continue.js fails
language/statements/for-of/generator-close-via-return.js fails
language/statements/for-of/generator-close-via-throw.js fails
-language/statements/for-of/head-const-bound-names-fordecl-tdz.js fails
-language/statements/for-of/head-let-bound-names-fordecl-tdz.js fails
language/statements/for-of/head-var-bound-names-let.js sloppyFails
language/statements/for-of/iterator-close-via-continue.js fails
language/statements/for-of/iterator-close-via-return.js fails
language/statements/for-of/iterator-close-via-throw.js fails
language/statements/for-of/iterator-next-reference.js fails
-language/statements/for-of/scope-body-lex-open.js fails
-language/statements/for-of/scope-head-lex-close.js fails
-language/statements/for-of/scope-head-lex-open.js fails
language/statements/for-of/yield-star-from-catch.js fails
language/statements/for-of/yield-star-from-finally.js fails
language/statements/for-of/yield-star-from-try.js fails
@@ -1465,19 +1194,9 @@ language/statements/generators/yield-star-before-newline.js fails
language/statements/if/tco-else-body.js strictFails
language/statements/if/tco-if-body.js strictFails
language/statements/labeled/tco.js strictFails
-language/statements/let/block-local-closure-get-before-initialization.js fails
language/statements/let/block-local-closure-set-before-initialization.js fails
-language/statements/let/block-local-use-before-initialization-in-declaration-statement.js fails
-language/statements/let/block-local-use-before-initialization-in-prior-statement.js fails
-language/statements/let/function-local-closure-get-before-initialization.js fails
language/statements/let/function-local-closure-set-before-initialization.js fails
-language/statements/let/function-local-use-before-initialization-in-declaration-statement.js fails
-language/statements/let/function-local-use-before-initialization-in-prior-statement.js fails
-language/statements/let/global-closure-get-before-initialization.js fails
language/statements/let/global-closure-set-before-initialization.js fails
-language/statements/let/global-use-before-initialization-in-declaration-statement.js fails
-language/statements/let/global-use-before-initialization-in-prior-statement.js fails
-language/statements/let/syntax/let.js fails
language/statements/return/tco.js strictFails
language/statements/switch/scope-lex-open-case.js fails
language/statements/switch/scope-lex-open-dflt.js fails
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 7ea652e070..f08d9a4798 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -978,7 +978,9 @@ void tst_QJSEngine::globalObjectProperties_enumerate()
<< "Uint32Array"
<< "Float32Array"
<< "Float64Array"
+ << "WeakSet"
<< "Set"
+ << "WeakMap"
<< "Map"
<< "Reflect"
<< "Proxy"
diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro
index 1bd23573b0..4d9e5c23c5 100644
--- a/tests/auto/qml/qml.pro
+++ b/tests/auto/qml/qml.pro
@@ -73,7 +73,8 @@ PRIVATETESTS += \
qv4mm \
qv4identifiertable \
ecmascripttests \
- bindingdependencyapi
+ bindingdependencyapi \
+ v4misc
qtHaveModule(widgets) {
PUBLICTESTS += \
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 69b62c733c..99306d8d15 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -356,6 +356,7 @@ private slots:
void callPropertyOnUndefined();
void jumpStrictNotEqualUndefined();
void removeBindingsWithNoDependencies();
+ void temporaryDeadZone();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -8798,6 +8799,19 @@ void tst_qqmlecmascript::removeBindingsWithNoDependencies()
}
+void tst_qqmlecmascript::temporaryDeadZone()
+{
+ QJSEngine engine;
+ QJSValue v = engine.evaluate(QString::fromLatin1("a; let a;"));
+ QVERIFY(v.isError());
+ v = engine.evaluate(QString::fromLatin1("a.name; let a;"));
+ QVERIFY(v.isError());
+ v = engine.evaluate(QString::fromLatin1("var a = {}; a[b]; let b;"));
+ QVERIFY(v.isError());
+ v = engine.evaluate(QString::fromLatin1("class C { constructor() { super[x]; let x; } }; new C()"));
+ QVERIFY(v.isError());
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/v4misc/tst_v4misc.cpp b/tests/auto/qml/v4misc/tst_v4misc.cpp
index 55a1f65608..22ff9c43fc 100644
--- a/tests/auto/qml/v4misc/tst_v4misc.cpp
+++ b/tests/auto/qml/v4misc/tst_v4misc.cpp
@@ -26,207 +26,82 @@
**
****************************************************************************/
-#include <qhashfunctions.h>
#include <qtest.h>
-
-#define V4_AUTOTEST
-#include <private/qv4ssa_p.h>
+#include <private/qv4instr_moth_p.h>
+#include <private/qv4script_p.h>
class tst_v4misc: public QObject
{
Q_OBJECT
private slots:
- void initTestCase();
-
- void rangeSplitting_1();
- void rangeSplitting_2();
- void rangeSplitting_3();
-
- void moveMapping_1();
- void moveMapping_2();
+ void tdzOptimizations_data();
+ void tdzOptimizations();
};
-using namespace QT_PREPEND_NAMESPACE(QV4::IR);
-
-void tst_v4misc::initTestCase()
-{
- qSetGlobalQHashSeed(0);
- QCOMPARE(qGlobalQHashSeed(), 0);
-}
-
-// split between two ranges
-void tst_v4misc::rangeSplitting_1()
+void tst_v4misc::tdzOptimizations_data()
{
- LifeTimeInterval interval;
- interval.addRange(59, 59);
- interval.addRange(61, 62);
- interval.addRange(64, 64);
- interval.addRange(69, 71);
- interval.validate();
- QCOMPARE(interval.end(), 71);
-
- LifeTimeInterval newInterval = interval.split(66, 70);
- interval.validate();
- newInterval.validate();
- QVERIFY(newInterval.isSplitFromInterval());
-
- QCOMPARE(interval.ranges().size(), 3);
- QCOMPARE(interval.ranges()[0].start, 59);
- QCOMPARE(interval.ranges()[0].end, 59);
- QCOMPARE(interval.ranges()[1].start, 61);
- QCOMPARE(interval.ranges()[1].end, 62);
- QCOMPARE(interval.ranges()[2].start, 64);
- QCOMPARE(interval.ranges()[2].end, 64);
- QCOMPARE(interval.end(), 70);
-
- QCOMPARE(newInterval.ranges().size(), 1);
- QCOMPARE(newInterval.ranges()[0].start, 70);
- QCOMPARE(newInterval.ranges()[0].end, 71);
- QCOMPARE(newInterval.end(), 71);
-}
+ QTest::addColumn<QString>("scriptToCompile");
-// split in the middle of a range
-void tst_v4misc::rangeSplitting_2()
-{
- LifeTimeInterval interval;
- interval.addRange(59, 59);
- interval.addRange(61, 64);
- interval.addRange(69, 71);
- interval.validate();
- QCOMPARE(interval.end(), 71);
-
- LifeTimeInterval newInterval = interval.split(62, 64);
- interval.validate();
- newInterval.validate();
- QVERIFY(newInterval.isSplitFromInterval());
-
- QCOMPARE(interval.ranges().size(), 2);
- QCOMPARE(interval.ranges()[0].start, 59);
- QCOMPARE(interval.ranges()[0].end, 59);
- QCOMPARE(interval.ranges()[1].start, 61);
- QCOMPARE(interval.ranges()[1].end, 62);
- QCOMPARE(interval.end(), 64);
-
- QCOMPARE(newInterval.ranges().size(), 2);
- QCOMPARE(newInterval.ranges()[0].start, 64);
- QCOMPARE(newInterval.ranges()[0].end, 64);
- QCOMPARE(newInterval.ranges()[1].start, 69);
- QCOMPARE(newInterval.ranges()[1].end, 71);
- QCOMPARE(newInterval.end(), 71);
+ QTest::newRow("access-after-let") << QString("let x; x = 10;");
+ QTest::newRow("access-after-const") << QString("const x = 10; print(x);");
+ QTest::newRow("access-after-let") << QString("for (let x of y) print(x);");
}
-// split in the middle of a range, and let it never go back to active again
-void tst_v4misc::rangeSplitting_3()
+void tst_v4misc::tdzOptimizations()
{
- LifeTimeInterval interval;
- interval.addRange(59, 59);
- interval.addRange(61, 64);
- interval.addRange(69, 71);
- interval.validate();
- QCOMPARE(interval.end(), 71);
-
- LifeTimeInterval newInterval = interval.split(64, LifeTimeInterval::InvalidPosition);
- interval.validate();
- newInterval.validate();
- QVERIFY(!newInterval.isValid());
-
- QCOMPARE(interval.ranges().size(), 2);
- QCOMPARE(interval.ranges()[0].start, 59);
- QCOMPARE(interval.ranges()[0].end, 59);
- QCOMPARE(interval.ranges()[1].start, 61);
- QCOMPARE(interval.ranges()[1].end, 64);
- QCOMPARE(interval.end(), 71);
-}
+ QFETCH(QString, scriptToCompile);
+
+ QV4::ExecutionEngine v4;
+ QV4::Script script(&v4, nullptr, scriptToCompile);
+ script.parse();
+ QVERIFY(!v4.hasException);
+
+ const auto function = script.compilationUnit->unitData()->functionAt(0);
+ const auto *code = function->code();
+ const auto len = function->codeSize;
+ const char *end = code + len;
+
+ const auto decodeInstruction = [&code]() {
+ QV4::Moth::Instr::Type type = QV4::Moth::Instr::Type(static_cast<uchar>(*code));
+ dispatch:
+ switch (type) {
+ case QV4::Moth::Instr::Type::Nop:
+ ++code;
+ type = QV4::Moth::Instr::Type(static_cast<uchar>(*code));
+ goto dispatch;
+ case QV4::Moth::Instr::Type::Nop_Wide: /* wide prefix */
+ ++code;
+ type = QV4::Moth::Instr::Type(0x100 | static_cast<uchar>(*code));
+ goto dispatch;
+
+#define CASE_AND_GOTO_INSTRUCTION(name, nargs, ...) \
+ case QV4::Moth::Instr::Type::name: \
+ MOTH_ADJUST_CODE(qint8, nargs); \
+ break;
+
+#define CASE_AND_GOTO_WIDE_INSTRUCTION(name, nargs, ...) \
+ case QV4::Moth::Instr::Type::name##_Wide: \
+ MOTH_ADJUST_CODE(int, nargs); \
+ type = QV4::Moth::Instr::Type::name; \
+ break;
+
+#define MOTH_DECODE_WITHOUT_ARGS(instr) \
+ INSTR_##instr(CASE_AND_GOTO) \
+ INSTR_##instr(CASE_AND_GOTO_WIDE)
+
+ FOR_EACH_MOTH_INSTR(MOTH_DECODE_WITHOUT_ARGS)
+ }
+ return type;
+ };
+
+ while (code < end) {
+ QV4::Moth::Instr::Type type = decodeInstruction();
+ QVERIFY(type != QV4::Moth::Instr::Type::DeadTemporalZoneCheck);
+ }
-void tst_v4misc::moveMapping_1()
-{
- Temp fp2(DoubleType, Temp::PhysicalRegister, 2);
- Temp fp3(DoubleType, Temp::PhysicalRegister, 3);
- Temp fp4(DoubleType, Temp::PhysicalRegister, 4);
- Temp fp5(DoubleType, Temp::PhysicalRegister, 5);
-
- MoveMapping mapping;
- mapping.add(&fp2, &fp3);
- mapping.add(&fp2, &fp4);
- mapping.add(&fp2, &fp5);
- mapping.add(&fp3, &fp2);
-
- mapping.order();
-// mapping.dump();
-
- QCOMPARE(mapping._moves.size(), 3);
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp4, false)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp5, false)));
- QVERIFY(mapping._moves.last() == MoveMapping::Move(&fp2, &fp3, true) ||
- mapping._moves.last() == MoveMapping::Move(&fp3, &fp2, true));
-}
-
-void tst_v4misc::moveMapping_2()
-{
- Temp fp1(DoubleType, Temp::PhysicalRegister, 1);
- Temp fp2(DoubleType, Temp::PhysicalRegister, 2);
- Temp fp3(DoubleType, Temp::PhysicalRegister, 3);
- Temp fp4(DoubleType, Temp::PhysicalRegister, 4);
- Temp fp5(DoubleType, Temp::PhysicalRegister, 5);
- Temp fp6(DoubleType, Temp::PhysicalRegister, 6);
- Temp fp7(DoubleType, Temp::PhysicalRegister, 7);
- Temp fp8(DoubleType, Temp::PhysicalRegister, 8);
- Temp fp9(DoubleType, Temp::PhysicalRegister, 9);
- Temp fp10(DoubleType, Temp::PhysicalRegister, 10);
- Temp fp11(DoubleType, Temp::PhysicalRegister, 11);
- Temp fp12(DoubleType, Temp::PhysicalRegister, 12);
- Temp fp13(DoubleType, Temp::PhysicalRegister, 13);
-
- MoveMapping mapping;
- mapping.add(&fp2, &fp1);
- mapping.add(&fp2, &fp3);
- mapping.add(&fp3, &fp2);
- mapping.add(&fp3, &fp4);
-
- mapping.add(&fp9, &fp8);
- mapping.add(&fp8, &fp7);
- mapping.add(&fp7, &fp6);
- mapping.add(&fp7, &fp5);
-
- mapping.add(&fp10, &fp11);
- mapping.add(&fp11, &fp12);
- mapping.add(&fp12, &fp13);
- mapping.add(&fp13, &fp10);
-
- mapping.order();
-// mapping.dump();
-
- QCOMPARE(mapping._moves.size(), 10);
-
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp1, false)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp3, &fp4, false)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp7, &fp6, false)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp7, &fp5, false)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp8, &fp7, false)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp9, &fp8, false)));
-
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp2, &fp3, true)) ||
- mapping._moves.contains(MoveMapping::Move(&fp3, &fp2, true)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp10, &fp13, true)) ||
- mapping._moves.contains(MoveMapping::Move(&fp13, &fp10, true)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp12, &fp13, true)) ||
- mapping._moves.contains(MoveMapping::Move(&fp13, &fp12, true)));
- QVERIFY(mapping._moves.contains(MoveMapping::Move(&fp12, &fp11, true)) ||
- mapping._moves.contains(MoveMapping::Move(&fp11, &fp12, true)));
-
- QVERIFY(!mapping._moves.at(0).needsSwap);
- QVERIFY(!mapping._moves.at(1).needsSwap);
- QVERIFY(!mapping._moves.at(2).needsSwap);
- QVERIFY(!mapping._moves.at(3).needsSwap);
- QVERIFY(!mapping._moves.at(4).needsSwap);
- QVERIFY(!mapping._moves.at(5).needsSwap);
- QVERIFY(mapping._moves.at(6).needsSwap);
- QVERIFY(mapping._moves.at(7).needsSwap);
- QVERIFY(mapping._moves.at(8).needsSwap);
- QVERIFY(mapping._moves.at(9).needsSwap);
}
-QTEST_MAIN(tst_v4misc)
+QTEST_MAIN(tst_v4misc);
#include "tst_v4misc.moc"