aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qv4compileddata.cpp16
-rw-r--r--src/qml/compiler/qv4compileddata_p.h8
-rw-r--r--src/qml/compiler/qv4compiler.cpp2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp17
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp53
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h35
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp231
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h26
-rw-r--r--src/qml/parser/qqmljslexer.cpp1
-rw-r--r--src/qml/parser/qqmljslexer_p.h3
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations64
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp10
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");