From 360a48aa3f5346aa7aaff741e4ef8f5dc8701f51 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 8 Apr 2018 14:28:40 +0200 Subject: Implement Symbol.for and Symbol.keyFor Change-Id: I5fde731b3a1a6d7c15154881ed82549b2800d104 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 2 +- src/qml/jsruntime/qv4engine_p.h | 2 + src/qml/jsruntime/qv4enginebase_p.h | 1 - src/qml/jsruntime/qv4identifiertable.cpp | 82 ++++++++++++++++++------- src/qml/jsruntime/qv4identifiertable_p.h | 9 ++- src/qml/jsruntime/qv4runtime.cpp | 2 + src/qml/jsruntime/qv4string.cpp | 8 ++- src/qml/jsruntime/qv4string_p.h | 42 +++++++------ src/qml/jsruntime/qv4symbol.cpp | 46 ++++++++++---- src/qml/jsruntime/qv4symbol_p.h | 4 +- tests/auto/qml/ecmascripttests/TestExpectations | 16 ----- 11 files changed, 135 insertions(+), 79 deletions(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5ede4373f2..87ae0e09d7 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -199,7 +199,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value); identifierTable = new IdentifierTable(this); - symbolTable = new IdentifierTable(this); memset(classes, 0, sizeof(classes)); classes[Class_Empty] = memoryManager->allocIC(); @@ -235,6 +234,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean")); jsStrings[String_number] = newIdentifier(QStringLiteral("number")); jsStrings[String_string] = newIdentifier(QStringLiteral("string")); + jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol")); jsStrings[String_object] = newIdentifier(QStringLiteral("object")); jsStrings[String_function] = newIdentifier(QStringLiteral("function")); jsStrings[String_length] = newIdentifier(QStringLiteral("length")); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index ff767a3dde..362d95b56a 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -279,6 +279,7 @@ public: String_boolean, String_number, String_string, + String_symbol, String_object, String_function, String_length, @@ -319,6 +320,7 @@ public: String *id_boolean() const { return reinterpret_cast(jsStrings + String_boolean); } String *id_number() const { return reinterpret_cast(jsStrings + String_number); } String *id_string() const { return reinterpret_cast(jsStrings + String_string); } + String *id_symbol() const { return reinterpret_cast(jsStrings + String_symbol); } String *id_object() const { return reinterpret_cast(jsStrings + String_object); } String *id_function() const { return reinterpret_cast(jsStrings + String_function); } String *id_length() const { return reinterpret_cast(jsStrings + String_length); } diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 4abf92eea5..085e44a913 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -83,7 +83,6 @@ struct Q_QML_EXPORT EngineBase { Value *jsStackBase = nullptr; IdentifierTable *identifierTable = nullptr; - IdentifierTable *symbolTable = nullptr; Object *globalObject = nullptr; // Exception handling diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index 46c631476a..c045ac3008 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -37,6 +37,7 @@ ** ****************************************************************************/ #include "qv4identifiertable_p.h" +#include "qv4symbol_p.h" QT_BEGIN_NAMESPACE @@ -59,8 +60,8 @@ IdentifierTable::IdentifierTable(ExecutionEngine *engine) , numBits(8) { alloc = primeForNumBits(numBits); - entriesByHash = (Heap::String **)malloc(alloc*sizeof(Heap::String *)); - entriesById = (Heap::String **)malloc(alloc*sizeof(Heap::String *)); + entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); + entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); memset(entriesByHash, 0, alloc*sizeof(Heap::String *)); memset(entriesById, 0, alloc*sizeof(Heap::String *)); } @@ -73,7 +74,7 @@ IdentifierTable::~IdentifierTable() h->identifierTable = nullptr; } -void IdentifierTable::addEntry(Heap::String *str) +void IdentifierTable::addEntry(Heap::StringOrSymbol *str) { uint hash = str->hashValue(); @@ -87,10 +88,10 @@ void IdentifierTable::addEntry(Heap::String *str) if (grow) { ++numBits; int newAlloc = primeForNumBits(numBits); - Heap::String **newEntries = (Heap::String **)malloc(newAlloc*sizeof(Heap::String *)); - memset(newEntries, 0, newAlloc*sizeof(Heap::String *)); + Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); + memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); for (int i = 0; i < alloc; ++i) { - Heap::String *e = entriesByHash[i]; + Heap::StringOrSymbol *e = entriesByHash[i]; if (!e) continue; uint idx = e->stringHash % newAlloc; @@ -103,10 +104,10 @@ void IdentifierTable::addEntry(Heap::String *str) free(entriesByHash); entriesByHash = newEntries; - newEntries = (Heap::String **)malloc(newAlloc*sizeof(Heap::String *)); - memset(newEntries, 0, newAlloc*sizeof(Heap::String *)); + newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); + memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); for (int i = 0; i < alloc; ++i) { - Heap::String *e = entriesById[i]; + Heap::StringOrSymbol *e = entriesById[i]; if (!e) continue; uint idx = e->identifier.id % newAlloc; @@ -146,9 +147,9 @@ Heap::String *IdentifierTable::insertString(const QString &s) uint subtype; uint hash = String::createHashValue(s.constData(), s.length(), &subtype); uint idx = hash % alloc; - while (Heap::String *e = entriesByHash[idx]) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == s) - return e; + return static_cast(e); ++idx; idx %= alloc; } @@ -160,6 +161,28 @@ Heap::String *IdentifierTable::insertString(const QString &s) return str; } +Heap::Symbol *IdentifierTable::insertSymbol(const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + + uint subtype; + uint hash = String::createHashValue(s.constData(), s.length(), &subtype); + uint idx = hash % alloc; + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { + if (e->stringHash == hash && e->toQString() == s) + return static_cast(e); + ++idx; + idx %= alloc; + } + + Heap::Symbol *str = Symbol::create(engine, s); + str->stringHash = hash; + str->subtype = subtype; + addEntry(str); + return str; + +} + Identifier IdentifierTable::identifierImpl(const Heap::String *str) { @@ -172,8 +195,8 @@ Identifier IdentifierTable::identifierImpl(const Heap::String *str) } uint idx = hash % alloc; - while (Heap::String *e = entriesByHash[idx]) { - if (e->stringHash == hash && e->isEqualTo(str)) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { + if (e->stringHash == hash && e->toQString() == str->toQString()) { str->identifier = e->identifier; return e->identifier; } @@ -185,7 +208,7 @@ Identifier IdentifierTable::identifierImpl(const Heap::String *str) return str->identifier; } -Heap::String *IdentifierTable::stringForId(Identifier i) const +Heap::StringOrSymbol *IdentifierTable::resolveId(Identifier i) const { uint arrayIdx = i.asArrayIndex(); if (arrayIdx < UINT_MAX) @@ -195,15 +218,28 @@ Heap::String *IdentifierTable::stringForId(Identifier i) const uint idx = i.id % alloc; while (1) { - Heap::String *e = entriesById[idx]; - Q_ASSERT(e); - if (e->identifier == i) + Heap::StringOrSymbol *e = entriesById[idx]; + if (!e || e->identifier == i) return e; ++idx; idx %= alloc; } } +Heap::String *IdentifierTable::stringForId(Identifier i) const +{ + Heap::StringOrSymbol *s = resolveId(i); + Q_ASSERT(s && s->internalClass->vtable->isString); + return static_cast(s); +} + +Heap::Symbol *IdentifierTable::symbolForId(Identifier i) const +{ + Heap::StringOrSymbol *s = resolveId(i); + Q_ASSERT(!s || !s->internalClass->vtable->isString); + return static_cast(s); +} + void IdentifierTable::markObjects(MarkStack *markStack) { for (const auto &h : idHashes) @@ -211,7 +247,7 @@ void IdentifierTable::markObjects(MarkStack *markStack) } template -int sweepTable(Heap::String **table, int alloc, std::function f) { +int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function f) { int freed = 0; Key lastKey = 0; int lastEntry = -1; @@ -224,7 +260,7 @@ int sweepTable(Heap::String **table, int alloc, std::functionisMarked()); @@ -257,8 +293,8 @@ int sweepTable(Heap::String **table, int alloc, std::function(entriesByHash, alloc, [](Heap::String *entry) {return entry->hashValue(); }); - int freed = sweepTable(entriesById, alloc, [](Heap::String *entry) {return entry->identifier.id; }); + int f = sweepTable(entriesByHash, alloc, [](Heap::StringOrSymbol *entry) {return entry->hashValue(); }); + int freed = sweepTable(entriesById, alloc, [](Heap::StringOrSymbol *entry) {return entry->identifier.id; }); Q_UNUSED(f); Q_ASSERT(f == freed); size -= freed; @@ -278,7 +314,7 @@ Identifier IdentifierTable::identifier(const char *s, int len) QLatin1String latin(s, len); uint idx = hash % alloc; - while (Heap::String *e = entriesByHash[idx]) { + while (Heap::StringOrSymbol *e = entriesByHash[idx]) { if (e->stringHash == hash && e->toQString() == latin) return e->identifier; ++idx; diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index 84e1ef2f4f..6e6600d055 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -67,12 +67,12 @@ struct IdentifierTable int alloc; int size; int numBits; - Heap::String **entriesByHash; - Heap::String **entriesById; + Heap::StringOrSymbol **entriesByHash; + Heap::StringOrSymbol **entriesById; QSet idHashes; - void addEntry(Heap::String *str); + void addEntry(Heap::StringOrSymbol *str); public: @@ -80,6 +80,7 @@ public: ~IdentifierTable(); Heap::String *insertString(const QString &s); + Heap::Symbol *insertSymbol(const QString &s); Identifier identifier(const Heap::String *str) { if (str->identifier.isValid()) @@ -95,7 +96,9 @@ public: Identifier identifierImpl(const Heap::String *str); + Heap::StringOrSymbol *resolveId(Identifier i) const; Q_QML_PRIVATE_EXPORT Heap::String *stringForId(Identifier i) const; + Q_QML_PRIVATE_EXPORT Heap::Symbol *symbolForId(Identifier i) const; void markObjects(MarkStack *markStack); void sweep(); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 106ea65679..18e5f29c09 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1174,6 +1174,8 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & case Value::Managed_Type: if (value.isString()) res = engine->id_string(); + else if (value.isSymbol()) + res = engine->id_symbol(); else if (value.objectValue()->as()) res = engine->id_function(); else diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index 78377286de..27c73a2b77 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -238,10 +238,12 @@ void Heap::String::append(const String *data, QChar *ch) } } -void Heap::String::createHashValue() const +void Heap::StringOrSymbol::createHashValue() const { - if (!text) - simplifyString(); + if (!text) { + Q_ASSERT(internalClass->vtable->isString); + static_cast(this)->simplifyString(); + } Q_ASSERT(text); const QChar *ch = reinterpret_cast(text->data()); const QChar *end = ch + text->size; diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index c48f46fe6e..bd94e1ee1a 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -66,8 +66,20 @@ namespace Heap { struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base { + enum StringType { + StringType_Symbol, + StringType_Regular, + StringType_ArrayIndex, + StringType_Unknown, + StringType_AddedString, + StringType_SubString, + StringType_Complex = StringType_AddedString + }; + mutable QStringData *text; mutable Identifier identifier; + mutable uint subtype; + mutable uint stringHash; static void markObjects(Heap::Base *that, MarkStack *markStack); void destroy(); @@ -79,18 +91,18 @@ struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base text->ref.ref(); return QString(ptr); } + void createHashValue() const; + inline unsigned hashValue() const { + if (subtype >= StringType_Unknown) + createHashValue(); + Q_ASSERT(subtype < StringType_Complex); + + return stringHash; + } }; struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { static void markObjects(Heap::Base *that, MarkStack *markStack); - enum StringType { - StringType_Regular, - StringType_ArrayIndex, - StringType_Unknown, - StringType_AddedString, - StringType_SubString, - StringType_Complex = StringType_AddedString - }; #ifndef V4_BOOTSTRAP const VTable *vtable() const { @@ -103,14 +115,6 @@ struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { std::size_t retainedTextSize() const { return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar)); } - void createHashValue() const; - inline unsigned hashValue() const { - if (subtype >= StringType_Unknown) - createHashValue(); - Q_ASSERT(subtype < StringType_Complex); - - return stringHash; - } inline QString toQString() const { if (subtype >= StringType_Complex) simplifyString(); @@ -134,8 +138,6 @@ struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol { bool startsWithUpper() const; - mutable uint subtype; - mutable uint stringHash; private: static void append(const String *data, QChar *ch); #endif @@ -282,7 +284,7 @@ public: uint h = toArrayIndex(ch, end); if (h != UINT_MAX) { if (subtype) - *subtype = Heap::String::StringType_ArrayIndex; + *subtype = Heap::StringOrSymbol::StringType_ArrayIndex; return h; } @@ -292,7 +294,7 @@ public: } if (subtype) - *subtype = Heap::String::StringType_Regular; + *subtype = (*ch == QLatin1Char('@')) ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular; return h; } }; diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp index 82602c7cf2..86d727ba07 100644 --- a/src/qml/jsruntime/qv4symbol.cpp +++ b/src/qml/jsruntime/qv4symbol.cpp @@ -39,16 +39,18 @@ #include #include +#include using namespace QV4; DEFINE_OBJECT_VTABLE(SymbolCtor); DEFINE_MANAGED_VTABLE(Symbol); -void Heap::Symbol::init(Heap::String *description) +void Heap::Symbol::init(const QString &s) { + Q_ASSERT(s.at(0) == QLatin1Char('@')); identifier = Identifier::fromHeapObject(this); - QString desc = description->toQString(); + QString desc(s); text = desc.data_ptr(); text->ref.ref(); } @@ -66,19 +68,32 @@ ReturnedValue QV4::SymbolCtor::call(const QV4::FunctionObject *f, const QV4::Val s = argv[0].toString(scope.engine); if (scope.hasException()) return Encode::undefined(); - return f->engine()->memoryManager->alloc(s->d())->asReturnedValue(); + QString desc = QLatin1Char('@') + s->toQString(); + return Symbol::create(scope.engine, desc)->asReturnedValue(); } -ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc) { -// Scope scope(f); -// ScopedValue k(s, argc ? argv[0]: Encode::undefined()); -// ScopedString key(scope, k->toString(scope.engine)); -// CHECK_EXCEPTION(); + Scope scope(f); + ScopedValue k(scope, argc ? argv[0]: Primitive::undefinedValue()); + ScopedString key(scope, k->toString(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + QString desc = QLatin1Char('@') + key->toQString(); + return scope.engine->identifierTable->insertSymbol(desc)->asReturnedValue(); } -ReturnedValue SymbolCtor::method_keyFor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +ReturnedValue SymbolCtor::method_keyFor(const FunctionObject *f, const Value *, const Value *argv, int argc) { + ExecutionEngine *e = f->engine(); + if (!argc || !argv[0].isSymbol()) + return e->throwTypeError(QLatin1String("Symbol.keyFor: Argument is not a symbol.")); + const Symbol &arg = static_cast(argv[0]); + Heap::Symbol *s = e->identifierTable->symbolForId(arg.identifier()); + Q_ASSERT(!s || s == arg.d()); + if (s) + return e->newString(arg.toQString().mid((1)))->asReturnedValue(); + return Encode::undefined(); } void SymbolPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -87,6 +102,9 @@ void SymbolPrototype::init(ExecutionEngine *engine, Object *ctor) ScopedValue v(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (v = this)); + ctor->defineDefaultProperty(QStringLiteral("for"), SymbolCtor::method_for, 1); + ctor->defineDefaultProperty(QStringLiteral("keyFor"), SymbolCtor::method_keyFor, 1); + defineDefaultProperty(QStringLiteral("toString"), method_toString); defineDefaultProperty(QStringLiteral("valueOf"), method_valueOf); } @@ -100,7 +118,7 @@ ReturnedValue SymbolPrototype::method_toString(const FunctionObject *f, const Va return e->newString(s->descriptiveString())->asReturnedValue(); } -ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) +ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int) { const Symbol *s = thisObject->as(); if (!s) { @@ -110,7 +128,13 @@ ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Val return s->asReturnedValue(); } +Heap::Symbol *Symbol::create(ExecutionEngine *e, const QString &s) +{ + Q_ASSERT(s.at(0) == QLatin1Char('@')); + return e->memoryManager->alloc(s); +} + QString Symbol::descriptiveString() const { - return QLatin1String("Symbol(") + toQString() + QLatin1String(")"); + return QLatin1String("Symbol(") + toQString().midRef(1) + QLatin1String(")"); } diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h index c33833a971..6ff60d22db 100644 --- a/src/qml/jsruntime/qv4symbol_p.h +++ b/src/qml/jsruntime/qv4symbol_p.h @@ -65,7 +65,7 @@ struct SymbolCtor : FunctionObject { }; struct Symbol : StringOrSymbol { - void init(Heap::String *description); + void init(const QString &s); }; } @@ -95,6 +95,8 @@ struct Symbol : StringOrSymbol V4_INTERNALCLASS(Symbol) V4_NEEDS_DESTROY + static Heap::Symbol *create(ExecutionEngine *e, const QString &s); + QString descriptiveString() const; }; diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 6f624c41f5..1dde3a2389 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -2620,26 +2620,15 @@ built-ins/String/symbol-string-coercion built-ins/Symbol/auto-boxing-non-strict built-ins/Symbol/auto-boxing-strict built-ins/Symbol/constructor -built-ins/Symbol/for/create-value built-ins/Symbol/for/cross-realm -built-ins/Symbol/for/length -built-ins/Symbol/for/name -built-ins/Symbol/for/prop-desc -built-ins/Symbol/for/retrieve-value -built-ins/Symbol/for/to-string-err built-ins/Symbol/hasInstance/cross-realm built-ins/Symbol/hasInstance/prop-desc built-ins/Symbol/isConcatSpreadable/cross-realm built-ins/Symbol/isConcatSpreadable/prop-desc built-ins/Symbol/iterator/cross-realm built-ins/Symbol/iterator/prop-desc -built-ins/Symbol/keyFor/arg-non-symbol -built-ins/Symbol/keyFor/arg-symbol-registry-hit built-ins/Symbol/keyFor/arg-symbol-registry-miss built-ins/Symbol/keyFor/cross-realm -built-ins/Symbol/keyFor/length -built-ins/Symbol/keyFor/name -built-ins/Symbol/keyFor/prop-desc built-ins/Symbol/length built-ins/Symbol/match/cross-realm built-ins/Symbol/match/prop-desc @@ -2652,14 +2641,9 @@ built-ins/Symbol/prototype/Symbol.toPrimitive/this-val-non-obj built-ins/Symbol/prototype/Symbol.toPrimitive/this-val-obj-symbol-wrapper built-ins/Symbol/prototype/Symbol.toPrimitive/this-val-symbol built-ins/Symbol/prototype/Symbol.toStringTag -built-ins/Symbol/prototype/toString/prop-desc built-ins/Symbol/prototype/toString/toString built-ins/Symbol/prototype/toString/undefined -built-ins/Symbol/prototype/valueOf/prop-desc -built-ins/Symbol/prototype/valueOf/this-val-non-obj -built-ins/Symbol/prototype/valueOf/this-val-obj-non-symbol built-ins/Symbol/prototype/valueOf/this-val-obj-symbol -built-ins/Symbol/prototype/valueOf/this-val-symbol built-ins/Symbol/replace/cross-realm built-ins/Symbol/replace/prop-desc built-ins/Symbol/search/cross-realm -- cgit v1.2.3