aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-08-08 13:15:40 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-15 14:24:23 +0000
commita1964f0b8e0569ef9ab754e2ec4c4d0559bc7681 (patch)
tree36416a7583527d87409ce0f9152687750a265e03 /src
parent15f56b74dc18c1105c9943f76599dbab5214b8e8 (diff)
Cleanup RegExpObject
Move properties from RegExpObject to getters in RegExp.prototype to be compliant with the JS spec. Implement support for the sticky flags ('y') and correctly parse the flags in the RegExp constructor. Change-Id: I5cf05d14e8139cf30d46235b8d466fb96084fcb7 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-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
10 files changed, 244 insertions, 148 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 {