diff options
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 16 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 8 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 17 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexp.cpp | 53 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexp_p.h | 35 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexpobject.cpp | 231 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4regexpobject_p.h | 26 | ||||
-rw-r--r-- | src/qml/parser/qqmljslexer.cpp | 1 | ||||
-rw-r--r-- | src/qml/parser/qqmljslexer_p.h | 3 | ||||
-rw-r--r-- | tests/auto/qml/ecmascripttests/TestExpectations | 64 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 10 |
12 files changed, 256 insertions, 210 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 798bfe921e..46034050e0 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -149,19 +149,9 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value)); for (uint i = 0; i < data->regexpTableSize; ++i) { const CompiledData::RegExp *re = data->regexpAt(i); - bool global = false; - bool multiline = false; - bool ignoreCase = false; - bool unicode = false; - if (re->flags & CompiledData::RegExp::RegExp_Global) - global = true; - if (re->flags & CompiledData::RegExp::RegExp_IgnoreCase) - ignoreCase = true; - if (re->flags & CompiledData::RegExp::RegExp_Multiline) - multiline = true; - if (re->flags & CompiledData::RegExp::RegExp_Unicode) - unicode = true; - runtimeRegularExpressions[i] = QV4::RegExp::create(engine, stringAt(re->stringIndex), ignoreCase, multiline, global, unicode); + uint f = re->flags; + const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f); + runtimeRegularExpressions[i] = QV4::RegExp::create(engine, stringAt(re->stringIndex), flags); } if (data->lookupTableSize) { diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 4b0e6422bb..7581fe3fff 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -137,15 +137,17 @@ static_assert(sizeof(Location) == 4, "Location structure needs to have the expec struct RegExp { enum Flags : unsigned int { + RegExp_NoFlags = 0x0, RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, RegExp_Multiline = 0x04, - RegExp_Unicode = 0x08 + RegExp_Unicode = 0x08, + RegExp_Sticky = 0x10 }; union { quint32 _dummy; - quint32_le_bitfield<0, 4> flags; - quint32_le_bitfield<4, 28> stringIndex; + quint32_le_bitfield<0, 5> flags; + quint32_le_bitfield<5, 27> stringIndex; }; RegExp() : _dummy(0) { } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 594ea32b51..4a726c366d 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -180,6 +180,8 @@ int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *r re.flags |= CompiledData::RegExp::RegExp_Multiline; if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode) re.flags |= CompiledData::RegExp::RegExp_Unicode; + if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky) + re.flags |= CompiledData::RegExp::RegExp_Sticky; regexps.append(re); return regexps.size() - 1; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index dbe1441cfc..f7189894b2 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -369,16 +369,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype()); ic = ic->addMember(id_lastIndex()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, &index); Q_ASSERT(index == RegExpObject::Index_LastIndex); - ic = ic->addMember((str = newIdentifier(QStringLiteral("source")))->propertyKey(), Attr_ReadOnly, &index); - Q_ASSERT(index == RegExpObject::Index_Source); - ic = ic->addMember((str = newIdentifier(QStringLiteral("global")))->propertyKey(), Attr_ReadOnly, &index); - Q_ASSERT(index == RegExpObject::Index_Global); - ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase")))->propertyKey(), Attr_ReadOnly, &index); - Q_ASSERT(index == RegExpObject::Index_IgnoreCase); - ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline")))->propertyKey(), Attr_ReadOnly, &index); - Q_ASSERT(index == RegExpObject::Index_Multiline); - ic = ic->addMember((str = newIdentifier(QStringLiteral("unicode")))->propertyKey(), Attr_ReadOnly, &index); - Q_ASSERT(index == RegExpObject::Index_Unicode); jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic->d()); classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d()); @@ -786,13 +776,8 @@ Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { - bool global = (flags & QV4::CompiledData::RegExp::RegExp_Global); - bool ignoreCase = (flags & QV4::CompiledData::RegExp::RegExp_IgnoreCase); - bool multiline = (flags & QV4::CompiledData::RegExp::RegExp_Multiline); - bool unicode = (flags & QV4::CompiledData::RegExp::RegExp_Unicode); - Scope scope(this); - Scoped<RegExp> re(scope, RegExp::create(this, pattern, ignoreCase, multiline, global, unicode)); + Scoped<RegExp> re(scope, RegExp::create(this, pattern, static_cast<CompiledData::RegExp::Flags>(flags))); return newRegExpObject(re); } diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 6963400a08..fe44720b8a 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -76,9 +76,25 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets); } -Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global, bool unicode) +QString Heap::RegExp::flagsAsString() const { - RegExpCacheKey key(pattern, ignoreCase, multiline, global); + QString result; + if (flags & CompiledData::RegExp::RegExp_Global) + result += QLatin1Char('g'); + if (flags & CompiledData::RegExp::RegExp_IgnoreCase) + result += QLatin1Char('i'); + if (flags & CompiledData::RegExp::RegExp_Multiline) + result += QLatin1Char('m'); + if (flags & CompiledData::RegExp::RegExp_Unicode) + result += QLatin1Char('u'); + if (flags & CompiledData::RegExp::RegExp_Sticky) + result += QLatin1Char('y'); + return result; +} + +Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, uint flags) +{ + RegExpCacheKey key(pattern, flags); RegExpCache *cache = engine->regExpCache; if (!cache) @@ -89,7 +105,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); Scope scope(engine); - Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline, global, unicode)); + Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, flags)); result->d()->cache = cache; cachedValue.set(engine, result); @@ -97,29 +113,28 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); } -void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, bool ignoreCase, bool multiline, bool global, bool unicode) +void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, uint flags) { Base::init(); this->pattern = new QString(pattern); - this->ignoreCase = ignoreCase; - this->multiLine = multiline; - this->unicode = unicode; - this->global = global; + this->flags = flags; valid = false; JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError; - JSC::RegExpFlags flags = JSC::NoFlags; - if (ignoreCase) - flags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagIgnoreCase); - if (multiline) - flags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagMultiline); - if (global) - flags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagGlobal); - if (unicode) - flags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagUnicode); - - JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), flags, error); + JSC::RegExpFlags jscFlags = JSC::NoFlags; + if (flags & CompiledData::RegExp::RegExp_Global) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagGlobal); + if (flags & CompiledData::RegExp::RegExp_IgnoreCase) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagIgnoreCase); + if (flags & CompiledData::RegExp::RegExp_Multiline) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagMultiline); + if (flags & CompiledData::RegExp::RegExp_Unicode) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagUnicode); + if (flags & CompiledData::RegExp::RegExp_Sticky) + jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagSticky); + + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), jscFlags, error); if (error != JSC::Yarr::ErrorCode::NoError) return; subPatternCount = yarrPattern.m_numSubpatterns; diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 9090aaa7d5..1e35d141ca 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -76,7 +76,7 @@ struct RegExpCacheKey; namespace Heap { struct RegExp : Base { - void init(ExecutionEngine *engine, const QString& pattern, bool ignoreCase, bool multiline, bool global, bool unicode); + void init(ExecutionEngine *engine, const QString& pattern, uint flags); void destroy(); QString *pattern; @@ -93,12 +93,10 @@ struct RegExp : Base { } RegExpCache *cache; int subPatternCount; - bool ignoreCase; - bool multiLine; - bool global; - bool unicode; + uint flags; bool valid; + QString flagsAsString() const; int captureCount() const { return subPatternCount + 1; } }; Q_STATIC_ASSERT(std::is_trivial< RegExp >::value); @@ -119,11 +117,13 @@ struct RegExp : public Managed #endif RegExpCache *cache() const { return d()->cache; } int subPatternCount() const { return d()->subPatternCount; } - bool ignoreCase() const { return d()->ignoreCase; } - bool multiLine() const { return d()->multiLine; } - bool global() const { return d()->global; } + bool ignoreCase() const { return d()->flags & CompiledData::RegExp::RegExp_IgnoreCase; } + bool multiLine() const { return d()->flags & CompiledData::RegExp::RegExp_Multiline; } + bool global() const { return d()->flags & CompiledData::RegExp::RegExp_Global; } + bool unicode() const { return d()->flags & CompiledData::RegExp::RegExp_Unicode; } + bool sticky() const { return d()->flags & CompiledData::RegExp::RegExp_Sticky; } - static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false, bool global = false, bool unicode = false); + static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, uint flags = CompiledData::RegExp::RegExp_NoFlags); bool isValid() const { return d()->valid; } @@ -136,30 +136,23 @@ struct RegExp : public Managed struct RegExpCacheKey { - RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine, bool global) - : pattern(pattern) - , ignoreCase(ignoreCase) - , multiLine(multiLine) - , global(global) + RegExpCacheKey(const QString &pattern, uint flags) + : pattern(pattern), flags(flags) { } explicit inline RegExpCacheKey(const RegExp::Data *re); bool operator==(const RegExpCacheKey &other) const - { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine && global == other.global; } + { return pattern == other.pattern && flags == other.flags;; } bool operator!=(const RegExpCacheKey &other) const { return !operator==(other); } QString pattern; - uint ignoreCase : 1; - uint multiLine : 1; - uint global : 1; + uint flags; }; inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re) : pattern(*re->pattern) - , ignoreCase(re->ignoreCase) - , multiLine(re->multiLine) - , global(re->global) + , flags(re->flags) {} inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index cff8223942..b54e73b85c 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -68,7 +68,7 @@ void Heap::RegExpObject::init() Object::init(); Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - value.set(scope.engine, QV4::RegExp::create(scope.engine, QString(), false, false)); + value.set(scope.engine, QV4::RegExp::create(scope.engine, QString(), CompiledData::RegExp::RegExp_NoFlags)); o->initProperties(); } @@ -128,8 +128,8 @@ void Heap::RegExpObject::init(const QRegExp &re) Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->d()->value.set(scope.engine, - QV4::RegExp::create(scope.engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false)); + uint flags = (re.caseSensitivity() == Qt::CaseInsensitive ? CompiledData::RegExp::RegExp_IgnoreCase : CompiledData::RegExp::RegExp_NoFlags); + o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, pattern, flags)); o->initProperties(); } @@ -139,20 +139,6 @@ void RegExpObject::initProperties() setProperty(Index_LastIndex, Primitive::fromInt32(0)); Q_ASSERT(value()); - - QString p = *value()->pattern; - if (p.isEmpty()) { - p = QStringLiteral("(?:)"); - } else { - // escape certain parts, see ch. 15.10.4 - p.replace('/', QLatin1String("\\/")); - } - - setProperty(Index_Source, engine()->newString(p)); - setProperty(Index_Global, Primitive::fromBoolean(global())); - setProperty(Index_IgnoreCase, Primitive::fromBoolean(value()->ignoreCase)); - setProperty(Index_Multiline, Primitive::fromBoolean(value()->multiLine)); - setProperty(Index_Unicode, Primitive::fromBoolean(value()->unicode)); } // Converts a JS RegExp to a QRegExp. @@ -160,20 +146,20 @@ void RegExpObject::initProperties() // have different semantics/flags, but we try to do our best. QRegExp RegExpObject::toQRegExp() const { - Qt::CaseSensitivity caseSensitivity = value()->ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive; + Qt::CaseSensitivity caseSensitivity = (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) ? Qt::CaseInsensitive : Qt::CaseSensitive; return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2); } QString RegExpObject::toString() const { - QString result = QLatin1Char('/') + source() + QLatin1Char('/'); - if (global()) - result += QLatin1Char('g'); - if (value()->ignoreCase) - result += QLatin1Char('i'); - if (value()->multiLine) - result += QLatin1Char('m'); - return result; + QString p = *value()->pattern; + if (p.isEmpty()) { + p = QStringLiteral("(?:)"); + } else { + // escape certain parts, see ch. 15.10.4 + p.replace('/', QLatin1String("\\/")); + } + return p; } QString RegExpObject::source() const @@ -186,16 +172,7 @@ QString RegExpObject::source() const uint RegExpObject::flags() const { - uint f = 0; - if (global()) - f |= QV4::RegExpObject::RegExp_Global; - if (value()->ignoreCase) - f |= QV4::RegExpObject::RegExp_IgnoreCase; - if (value()->multiLine) - f |= QV4::RegExpObject::RegExp_Multiline; - if (value()->unicode) - f |= QV4::RegExpObject::RegExp_Unicode; - return f; + return d()->value->flags; } ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *str) @@ -286,28 +263,30 @@ ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, con if (scope.hasException()) return Encode::undefined(); - bool global = false; - bool ignoreCase = false; - bool multiLine = false; + uint flags = CompiledData::RegExp::RegExp_NoFlags; if (!f->isUndefined()) { ScopedString s(scope, f->toString(scope.engine)); if (scope.hasException()) return Encode::undefined(); QString str = s->toQString(); for (int i = 0; i < str.length(); ++i) { - if (str.at(i) == QLatin1Char('g') && !global) { - global = true; - } else if (str.at(i) == QLatin1Char('i') && !ignoreCase) { - ignoreCase = true; - } else if (str.at(i) == QLatin1Char('m') && !multiLine) { - multiLine = true; + if (str.at(i) == QLatin1Char('g') && !(flags & CompiledData::RegExp::RegExp_Global)) { + flags |= CompiledData::RegExp::RegExp_Global; + } else if (str.at(i) == QLatin1Char('i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) { + flags |= CompiledData::RegExp::RegExp_IgnoreCase; + } else if (str.at(i) == QLatin1Char('m') && !(flags & CompiledData::RegExp::RegExp_Multiline)) { + flags |= CompiledData::RegExp::RegExp_Multiline; + } else if (str.at(i) == QLatin1Char('u') && !(flags & CompiledData::RegExp::RegExp_Unicode)) { + flags |= CompiledData::RegExp::RegExp_Unicode; + } else if (str.at(i) == QLatin1Char('y') && !(flags & CompiledData::RegExp::RegExp_Sticky)) { + flags |= CompiledData::RegExp::RegExp_Sticky; } else { return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); } } } - Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine, global)); + Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, flags)); if (!regexp->isValid()) { return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); } @@ -357,11 +336,20 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) ctor->defineAccessorProperty(QStringLiteral("$'"), method_get_rightContext, nullptr); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); + defineAccessorProperty(QStringLiteral("flags"), method_get_flags, nullptr); + defineAccessorProperty(QStringLiteral("global"), method_get_global, nullptr); + defineAccessorProperty(QStringLiteral("ignoreCase"), method_get_ignoreCase, nullptr); defineDefaultProperty(QStringLiteral("exec"), method_exec, 1); + defineDefaultProperty(engine->symbol_match(), method_match, 1); + defineAccessorProperty(QStringLiteral("multiline"), method_get_multiline, nullptr); + defineAccessorProperty(QStringLiteral("source"), method_get_source, nullptr); + defineAccessorProperty(QStringLiteral("sticky"), method_get_sticky, nullptr); defineDefaultProperty(QStringLiteral("test"), method_test, 1); defineDefaultProperty(engine->id_toString(), method_toString, 0); + defineAccessorProperty(QStringLiteral("unicode"), method_get_unicode, nullptr); + + // another web extension defineDefaultProperty(QStringLiteral("compile"), method_compile, 2); - defineDefaultProperty(engine->symbol_match(), method_match, 1); } /* used by String.match */ @@ -442,33 +430,64 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value return r->builtinExec(scope.engine, str); } -ReturnedValue RegExpPrototype::method_test(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue RegExpPrototype::method_get_flags(const FunctionObject *f, const Value *thisObject, const Value *, int) { - Value res = Value::fromReturnedValue(method_exec(b, thisObject, argv, argc)); - return Encode(!res.isNull()); + Scope scope(f); + ScopedObject o(scope, thisObject); + if (!o) + return scope.engine->throwTypeError(); + + QString result; + ScopedValue v(scope); + ScopedString key(scope); + v = o->get((key = scope.engine->newString(QStringLiteral("global")))); + if (scope.hasException()) + return Encode::undefined(); + if (v->toBoolean()) + result += QLatin1Char('g'); + v = o->get((key = scope.engine->newString(QStringLiteral("ignoreCase")))); + if (scope.hasException()) + return Encode::undefined(); + if (v->toBoolean()) + result += QLatin1Char('i'); + v = o->get((key = scope.engine->newString(QStringLiteral("multiline")))); + if (scope.hasException()) + return Encode::undefined(); + if (v->toBoolean()) + result += QLatin1Char('m'); + v = o->get((key = scope.engine->newString(QStringLiteral("unicode")))); + if (scope.hasException()) + return Encode::undefined(); + if (v->toBoolean()) + result += QLatin1Char('u'); + v = o->get((key = scope.engine->newString(QStringLiteral("sticky")))); + if (scope.hasException()) + return Encode::undefined(); + if (v->toBoolean()) + result += QLatin1Char('y'); + return scope.engine->newString(result)->asReturnedValue(); } -ReturnedValue RegExpPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) +ReturnedValue RegExpPrototype::method_get_global(const FunctionObject *f, const Value *thisObject, const Value *, int) { - ExecutionEngine *v4 = b->engine(); - const RegExpObject *r = thisObject->as<RegExpObject>(); - if (!r) - return v4->throwTypeError(); + Scope scope(f); + Scoped<RegExpObject> re(scope, thisObject); + if (!re) + return scope.engine->throwTypeError(); - return Encode(v4->newString(r->toString())); + bool b = re->value()->flags & CompiledData::RegExp::RegExp_Global; + return Encode(b); } -ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +ReturnedValue RegExpPrototype::method_get_ignoreCase(const FunctionObject *f, const Value *thisObject, const Value *, int) { - Scope scope(b); - Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>()); - if (!r) + Scope scope(f); + Scoped<RegExpObject> re(scope, thisObject); + if (!re) return scope.engine->throwTypeError(); - Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc)); - - r->d()->value.set(scope.engine, re->value()); - return Encode::undefined(); + bool b = re->value()->flags & CompiledData::RegExp::RegExp_IgnoreCase; + return Encode(b); } ReturnedValue RegExpPrototype::method_match(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) @@ -490,6 +509,90 @@ ReturnedValue RegExpPrototype::method_match(const FunctionObject *f, const Value return scope.engine->throwTypeError(); } +ReturnedValue RegExpPrototype::method_get_multiline(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<RegExpObject> re(scope, thisObject); + if (!re) + return scope.engine->throwTypeError(); + + bool b = re->value()->flags & CompiledData::RegExp::RegExp_Multiline; + return Encode(b); +} + +ReturnedValue RegExpPrototype::method_get_source(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<RegExpObject> re(scope, thisObject); + if (!re) + return scope.engine->throwTypeError(); + + return scope.engine->newString(re->toString())->asReturnedValue(); +} + +ReturnedValue RegExpPrototype::method_get_sticky(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<RegExpObject> re(scope, thisObject); + if (!re) + return scope.engine->throwTypeError(); + + bool b = re->value()->flags & CompiledData::RegExp::RegExp_Sticky; + return Encode(b); +} + +ReturnedValue RegExpPrototype::method_test(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Value res = Value::fromReturnedValue(method_exec(b, thisObject, argv, argc)); + return Encode(!res.isNull()); +} + +ReturnedValue RegExpPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) +{ + Scope scope(b); + const Object *r = thisObject->as<Object>(); + if (!r) + return scope.engine->throwTypeError(); + + ScopedString key(scope); + ScopedValue v(scope); + v = r->get((key = scope.engine->newString(QStringLiteral("source")))); + ScopedString source(scope, v->toString(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + v = r->get((key = scope.engine->newString(QStringLiteral("flags")))); + ScopedString flags(scope, v->toString(scope.engine)); + if (scope.hasException()) + return Encode::undefined(); + + QString result = QLatin1Char('/') + source->toQString() + QLatin1Char('/') + flags->toQString(); + return Encode(scope.engine->newString(result)); +} + +ReturnedValue RegExpPrototype::method_get_unicode(const FunctionObject *f, const Value *thisObject, const Value *, int) +{ + Scope scope(f); + Scoped<RegExpObject> re(scope, thisObject); + if (!re) + return scope.engine->throwTypeError(); + + bool b = re->value()->flags & CompiledData::RegExp::RegExp_Unicode; + return Encode(b); +} + +ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) +{ + Scope scope(b); + Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>()); + if (!r) + return scope.engine->throwTypeError(); + + Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc)); + + r->d()->value.set(scope.engine, re->value()); + return Encode::undefined(); +} + template <uint index> ReturnedValue RegExpPrototype::method_get_lastMatch_n(const FunctionObject *b, const Value *, const Value *, int) { diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 74ea8aea4e..71936e2450 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -76,7 +76,7 @@ namespace Heap { Member(class, Pointer, RegExp *, value) DECLARE_HEAP_OBJECT(RegExpObject, Object) { - DECLARE_MARKOBJECTS(RegExpObject); + DECLARE_MARKOBJECTS(RegExpObject) void init(); void init(QV4::RegExp *value); @@ -109,16 +109,12 @@ struct RegExpObject: Object { RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, RegExp_Multiline = 0x04, - RegExp_Unicode = 0x08 + RegExp_Unicode = 0x08, + RegExp_Sticky = 0x10 }; enum { Index_LastIndex = 0, - Index_Source = 1, - Index_Global = 2, - Index_IgnoreCase = 3, - Index_Multiline = 4, - Index_Unicode = 5, Index_ArrayIndex = Heap::ArrayObject::LengthPropertyIndex + 1, Index_ArrayInput = Index_ArrayIndex + 1 }; @@ -126,7 +122,7 @@ struct RegExpObject: Object { enum { NInlineProperties = 5 }; Heap::RegExp *value() const { return d()->value; } - bool global() const { return d()->value->global; } + bool global() const { return d()->value->flags & CompiledData::RegExp::RegExp_Global; } void initProperties(); @@ -165,13 +161,21 @@ struct RegExpPrototype: RegExpObject void init(ExecutionEngine *engine, Object *ctor); static ReturnedValue method_exec(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_flags(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_global(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_ignoreCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_multiline(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_source(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_sticky(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_test(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - - static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue method_get_unicode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + // Web extension + static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); + // properties on the constructor, web extensions template <uint index> static ReturnedValue method_get_lastMatch_n(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index e93dda942a..b98bb8bac5 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -59,6 +59,7 @@ static inline int regExpFlagFromChar(const QChar &ch) case 'i': return Lexer::RegExp_IgnoreCase; case 'm': return Lexer::RegExp_Multiline; case 'u': return Lexer::RegExp_Unicode; + case 'y': return Lexer::RegExp_Sticky; } return 0; } diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index 64db62625a..644c5c09aa 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -114,7 +114,8 @@ public: RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, RegExp_Multiline = 0x04, - RegExp_Unicode = 0x08 + RegExp_Unicode = 0x08, + RegExp_Sticky = 0x10 }; enum ParseModeFlags { diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 77397328e0..85e2e72ac7 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -406,7 +406,6 @@ built-ins/Function/prototype/toString/async-method-object.js fails built-ins/Function/prototype/toString/intrinsics.js fails built-ins/Function/prototype/toString/method-computed-property-name.js fails built-ins/Function/prototype/toString/proxy.js fails -built-ins/Function/prototype/toString/symbol-named-builtins.js fails built-ins/GeneratorFunction/proto-from-ctor-realm.js fails built-ins/JSON/parse/revived-proxy-revoked.js fails built-ins/JSON/parse/revived-proxy.js fails @@ -464,10 +463,6 @@ built-ins/Object/create/15.2.3.5-4-37.js strictFails built-ins/Object/entries/getter-making-future-key-nonenumerable.js fails built-ins/Object/entries/getter-removing-future-key.js fails built-ins/Object/entries/observable-operations.js fails -built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-212.js fails -built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-213.js fails -built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-214.js fails -built-ins/Object/getOwnPropertyDescriptor/15.2.3.3-4-215.js fails built-ins/Object/getOwnPropertyDescriptors/proxy-undefined-descriptor.js fails built-ins/Object/getOwnPropertyDescriptors/symbols-included.js fails built-ins/Object/keys/proxy-keys.js fails @@ -715,14 +710,11 @@ built-ins/RegExp/proto-from-ctor-realm.js fails built-ins/RegExp/prototype/15.10.6.js fails built-ins/RegExp/prototype/Symbol.match/builtin-coerce-lastindex.js fails built-ins/RegExp/prototype/Symbol.match/builtin-failure-g-set-lastindex.js fails -built-ins/RegExp/prototype/Symbol.match/builtin-failure-y-return-val.js fails +built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails built-ins/RegExp/prototype/Symbol.match/builtin-failure-y-set-lastindex-err.js fails built-ins/RegExp/prototype/Symbol.match/builtin-failure-y-set-lastindex.js fails -built-ins/RegExp/prototype/Symbol.match/builtin-infer-unicode.js fails -built-ins/RegExp/prototype/Symbol.match/builtin-success-g-set-lastindex.js fails built-ins/RegExp/prototype/Symbol.match/builtin-success-y-set-lastindex-err.js fails built-ins/RegExp/prototype/Symbol.match/builtin-success-y-set-lastindex.js fails -built-ins/RegExp/prototype/Symbol.match/builtin-y-coerce-lastindex-err.js fails built-ins/RegExp/prototype/Symbol.match/coerce-global.js fails built-ins/RegExp/prototype/Symbol.match/g-coerce-result-err.js fails built-ins/RegExp/prototype/Symbol.match/g-get-exec-err.js fails @@ -738,8 +730,6 @@ built-ins/RegExp/prototype/Symbol.match/get-unicode-error.js fails built-ins/RegExp/prototype/Symbol.match/u-advance-after-empty.js fails built-ins/RegExp/prototype/Symbol.match/y-fail-global-return.js fails built-ins/RegExp/prototype/Symbol.match/y-fail-lastindex-no-write.js fails -built-ins/RegExp/prototype/Symbol.match/y-fail-lastindex.js fails -built-ins/RegExp/prototype/Symbol.match/y-fail-return.js fails built-ins/RegExp/prototype/Symbol.match/y-init-lastindex.js fails built-ins/RegExp/prototype/Symbol.match/y-set-lastindex.js fails built-ins/RegExp/prototype/Symbol.replace/arg-1-coerce-err.js fails @@ -789,7 +779,6 @@ built-ins/RegExp/prototype/Symbol.replace/subst-dollar.js fails built-ins/RegExp/prototype/Symbol.replace/subst-matched.js fails built-ins/RegExp/prototype/Symbol.replace/u-advance-after-empty.js fails built-ins/RegExp/prototype/Symbol.replace/y-fail-global-return.js fails -built-ins/RegExp/prototype/Symbol.replace/y-fail-lastindex-no-write.js fails built-ins/RegExp/prototype/Symbol.replace/y-fail-lastindex.js fails built-ins/RegExp/prototype/Symbol.replace/y-fail-return.js fails built-ins/RegExp/prototype/Symbol.replace/y-init-lastindex.js fails @@ -857,63 +846,29 @@ built-ins/RegExp/prototype/Symbol.split/u-lastindex-adv-thru-match.js fails built-ins/RegExp/prototype/exec/S15.10.6.2_A5_T3.js fails built-ins/RegExp/prototype/exec/failure-lastindex-access.js fails built-ins/RegExp/prototype/exec/success-lastindex-access.js fails -built-ins/RegExp/prototype/exec/u-lastindex-adv.js fails built-ins/RegExp/prototype/exec/y-fail-lastindex-no-write.js fails -built-ins/RegExp/prototype/exec/y-fail-lastindex.js fails -built-ins/RegExp/prototype/exec/y-fail-return.js fails built-ins/RegExp/prototype/exec/y-init-lastindex.js fails built-ins/RegExp/prototype/exec/y-set-lastindex.js fails -built-ins/RegExp/prototype/flags/coercion-global.js fails -built-ins/RegExp/prototype/flags/coercion-ignoreCase.js fails -built-ins/RegExp/prototype/flags/coercion-multiline.js fails -built-ins/RegExp/prototype/flags/coercion-sticky.js fails -built-ins/RegExp/prototype/flags/coercion-unicode.js fails -built-ins/RegExp/prototype/flags/length.js fails -built-ins/RegExp/prototype/flags/name.js fails -built-ins/RegExp/prototype/flags/prop-desc.js fails -built-ins/RegExp/prototype/flags/this-val-non-obj.js fails -built-ins/RegExp/prototype/flags/this-val-regexp-prototype.js fails -built-ins/RegExp/prototype/global/15.10.7.2-2.js fails -built-ins/RegExp/prototype/global/S15.10.7.2_A9.js fails -built-ins/RegExp/prototype/global/length.js fails -built-ins/RegExp/prototype/global/name.js fails built-ins/RegExp/prototype/global/this-val-regexp-prototype.js fails -built-ins/RegExp/prototype/ignoreCase/15.10.7.3-2.js fails -built-ins/RegExp/prototype/ignoreCase/S15.10.7.3_A9.js fails -built-ins/RegExp/prototype/ignoreCase/length.js fails -built-ins/RegExp/prototype/ignoreCase/name.js fails built-ins/RegExp/prototype/ignoreCase/this-val-regexp-prototype.js fails -built-ins/RegExp/prototype/multiline/15.10.7.4-2.js fails -built-ins/RegExp/prototype/multiline/S15.10.7.4_A9.js fails -built-ins/RegExp/prototype/multiline/length.js fails -built-ins/RegExp/prototype/multiline/name.js fails built-ins/RegExp/prototype/multiline/this-val-regexp-prototype.js fails built-ins/RegExp/prototype/no-regexp-matcher.js fails -built-ins/RegExp/prototype/source/length.js fails -built-ins/RegExp/prototype/source/name.js fails -built-ins/RegExp/prototype/source/prop-desc.js fails -built-ins/RegExp/prototype/source/this-val-regexp-prototype.js fails built-ins/RegExp/prototype/source/value-line-terminator.js fails -built-ins/RegExp/prototype/sticky/length.js fails -built-ins/RegExp/prototype/sticky/name.js fails -built-ins/RegExp/prototype/sticky/prop-desc.js fails -built-ins/RegExp/prototype/sticky/this-val-invalid-obj.js fails -built-ins/RegExp/prototype/sticky/this-val-non-obj.js fails built-ins/RegExp/prototype/sticky/this-val-regexp-prototype.js fails -built-ins/RegExp/prototype/sticky/this-val-regexp.js fails built-ins/RegExp/prototype/test/S15.10.6.3_A1_T22.js fails built-ins/RegExp/prototype/test/y-fail-lastindex-no-write.js fails -built-ins/RegExp/prototype/test/y-fail-lastindex.js fails -built-ins/RegExp/prototype/test/y-fail-return.js fails built-ins/RegExp/prototype/test/y-init-lastindex.js fails built-ins/RegExp/prototype/test/y-set-lastindex.js fails -built-ins/RegExp/prototype/unicode/length.js fails -built-ins/RegExp/prototype/unicode/name.js fails -built-ins/RegExp/prototype/unicode/prop-desc.js fails built-ins/RegExp/prototype/unicode/this-val-regexp-prototype.js fails -built-ins/RegExp/prototype/unicode/this-val-regexp.js fails +built-ins/RegExp/unicode_restricted_brackets.js fails +built-ins/RegExp/unicode_restricted_character_class_escape.js fails +built-ins/RegExp/unicode_restricted_identity_escape.js fails +built-ins/RegExp/unicode_restricted_identity_escape_alpha.js fails +built-ins/RegExp/unicode_restricted_identity_escape_c.js fails +built-ins/RegExp/unicode_restricted_incomple_quantifier.js fails +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/RegExp/valid-flags-y.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 @@ -2139,7 +2094,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/literals/regexp/u-case-mapping.js fails language/literals/regexp/y-assertion-start.js fails language/module-code/eval-export-dflt-cls-anon.js strictFails language/module-code/eval-gtbndng-indirect-update-dflt.js strictFails diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 04a93fe436..54b11c6215 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -514,12 +514,11 @@ void tst_QJSEngine::newRegExp() // prototype should be RegExp.prototype QVERIFY(!rexp.prototype().isUndefined()); QCOMPARE(rexp.prototype().isObject(), true); - QCOMPARE(rexp.prototype().isRegExp(), true); // Get [[Class]] internal property of RegExp Prototype Object. // See ECMA-262 Section 8.6.2, "Object Internal Properties and Methods". // See ECMA-262 Section 15.10.6, "Properties of the RegExp Prototype Object". QJSValue r = eng.evaluate("Object.prototype.toString.call(RegExp.prototype)"); - QCOMPARE(r.toString(), QString::fromLatin1("[object RegExp]")); + QCOMPARE(r.toString(), QString::fromLatin1("[object Object]")); QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); QCOMPARE(qjsvalue_cast<QRegExp>(rexp).pattern(), QRegExp("foo").pattern()); @@ -545,8 +544,7 @@ void tst_QJSEngine::jsRegExp() QVERIFY(r2.strictlyEquals(r)); QJSValue r3 = rxCtor.call(QJSValueList() << r << "gim"); - QVERIFY(r3.isError()); - QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another + QVERIFY(!r3.isError()); QJSValue r4 = rxCtor.call(QJSValueList() << "foo" << "gim"); QVERIFY(r4.isRegExp()); @@ -554,9 +552,7 @@ void tst_QJSEngine::jsRegExp() QJSValue r5 = rxCtor.callAsConstructor(QJSValueList() << r); QVERIFY(r5.isRegExp()); QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim")); - // In JSC, constructing a RegExp from another produces the same identical object. - // This is different from SpiderMonkey and old back-end. - QVERIFY(!r5.strictlyEquals(r)); + QVERIFY(r5.strictlyEquals(r)); // See ECMA-262 Section 15.10.4.1, "new RegExp(pattern, flags)". QJSValue r6 = rxCtor.callAsConstructor(QJSValueList() << "foo" << "bar"); |