diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-08-30 01:00:07 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2018-08-30 01:00:07 +0200 |
commit | 5ff4facd8efc41b30b5ffa5abb6a12406685ca0b (patch) | |
tree | 057d345a90e04dc222eed5cc0e17fce86605b842 | |
parent | 120bf8a5d1769279d4e9d8378543f0adfc4a50d0 (diff) | |
parent | f738abe53ed96d0d4e29290bc04b270cfc74569b (diff) |
Merge remote-tracking branch 'origin/5.12' into dev
Change-Id: If28421324777e5165f63c9f856cb6d133709c799
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, ®istersInTDZ](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" |