aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-04-08 14:28:40 +0200
committerLars Knoll <lars.knoll@qt.io>2018-05-02 14:18:41 +0000
commit360a48aa3f5346aa7aaff741e4ef8f5dc8701f51 (patch)
tree4ba4fc520a1387c24a667ebd51203cc6cd1e5996 /src
parentbab5e5adf578fb37402ff03f4bd5e9ed3ce4c2d2 (diff)
Implement Symbol.for and Symbol.keyFor
Change-Id: I5fde731b3a1a6d7c15154881ed82549b2800d104 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp2
-rw-r--r--src/qml/jsruntime/qv4engine_p.h2
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h1
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp82
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h9
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp2
-rw-r--r--src/qml/jsruntime/qv4string.cpp8
-rw-r--r--src/qml/jsruntime/qv4string_p.h42
-rw-r--r--src/qml/jsruntime/qv4symbol.cpp46
-rw-r--r--src/qml/jsruntime/qv4symbol_p.h4
10 files changed, 135 insertions, 63 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<InternalClass>();
@@ -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<String *>(jsStrings + String_boolean); }
String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); }
String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); }
+ String *id_symbol() const { return reinterpret_cast<String *>(jsStrings + String_symbol); }
String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); }
String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); }
String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); }
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<Heap::String *>(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<Heap::Symbol *>(e);
+ ++idx;
+ idx %= alloc;
+ }
+
+ Heap::Symbol *str = Symbol::create(engine, s);
+ str->stringHash = hash;
+ str->subtype = subtype;
+ addEntry(str);
+ return str;
+
+}
+
Identifier IdentifierTable::identifierImpl(const Heap::String *str)
{
@@ -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<Heap::String *>(s);
+}
+
+Heap::Symbol *IdentifierTable::symbolForId(Identifier i) const
+{
+ Heap::StringOrSymbol *s = resolveId(i);
+ Q_ASSERT(!s || !s->internalClass->vtable->isString);
+ return static_cast<Heap::Symbol *>(s);
+}
+
void IdentifierTable::markObjects(MarkStack *markStack)
{
for (const auto &h : idHashes)
@@ -211,7 +247,7 @@ void IdentifierTable::markObjects(MarkStack *markStack)
}
template <typename Key>
-int sweepTable(Heap::String **table, int alloc, std::function<Key(Heap::String *)> f) {
+int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::StringOrSymbol *)> f) {
int freed = 0;
Key lastKey = 0;
int lastEntry = -1;
@@ -224,7 +260,7 @@ int sweepTable(Heap::String **table, int alloc, std::function<Key(Heap::String *
for (int i = 0; i < alloc; ++i) {
int idx = (i + start) % alloc;
- Heap::String *entry = table[idx];
+ Heap::StringOrSymbol *entry = table[idx];
if (!entry) {
lastEntry = -1;
continue;
@@ -247,7 +283,7 @@ int sweepTable(Heap::String **table, int alloc, std::function<Key(Heap::String *
++freed;
}
for (int i = 0; i < alloc; ++i) {
- Heap::String *entry = table[i];
+ Heap::StringOrSymbol *entry = table[i];
if (!entry)
continue;
Q_ASSERT(entry->isMarked());
@@ -257,8 +293,8 @@ int sweepTable(Heap::String **table, int alloc, std::function<Key(Heap::String *
void IdentifierTable::sweep()
{
- int f = sweepTable<int>(entriesByHash, alloc, [](Heap::String *entry) {return entry->hashValue(); });
- int freed = sweepTable<quint64>(entriesById, alloc, [](Heap::String *entry) {return entry->identifier.id; });
+ int f = sweepTable<int>(entriesByHash, alloc, [](Heap::StringOrSymbol *entry) {return entry->hashValue(); });
+ int freed = sweepTable<quint64>(entriesById, alloc, [](Heap::StringOrSymbol *entry) {return entry->identifier.id; });
Q_UNUSED(f);
Q_ASSERT(f == freed);
size -= freed;
@@ -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<IdentifierHashData *> 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<FunctionObject>())
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<const Heap::String *>(this)->simplifyString();
+ }
Q_ASSERT(text);
const QChar *ch = reinterpret_cast<const QChar *>(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 <qv4symbol_p.h>
#include <qv4functionobject_p.h>
+#include <qv4identifiertable_p.h>
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<Symbol>(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<const Symbol &>(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<Symbol>();
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<Symbol>(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;
};