aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-08-10 09:21:11 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-15 14:24:45 +0000
commit5819700e0c2e17f8aa3e02bbfdf1d760906d02a4 (patch)
treefe9428685a6e8086ea14ed61d65d0164027397b0 /src
parentb862d429b1010fecf2e29d0e781b012a4b07cad6 (diff)
Fix spec compliance for the RegExp constructor
Change-Id: I767b27faab912e91962797ca154d929473113cc1 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp2
-rw-r--r--src/qml/jsruntime/qv4engine_p.h4
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp101
3 files changed, 76 insertions, 31 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 5db26857e4..e92d7c69ba 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -291,6 +291,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
jsStrings[String_multiline] = newIdentifier(QStringLiteral("multiline"));
jsStrings[String_unicode] = newIdentifier(QStringLiteral("unicode"));
jsStrings[String_sticky] = newIdentifier(QStringLiteral("sticky"));
+ jsStrings[String_source] = newIdentifier(QStringLiteral("source"));
+ jsStrings[String_flags] = newIdentifier(QStringLiteral("flags"));
jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance"));
jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable"));
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 3a782ae097..00879511d3 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -327,6 +327,8 @@ public:
String_multiline,
String_unicode,
String_sticky,
+ String_source,
+ String_flags,
NJSStrings
};
@@ -395,6 +397,8 @@ public:
String *id_multiline() const { return reinterpret_cast<String *>(jsStrings + String_multiline); }
String *id_unicode() const { return reinterpret_cast<String *>(jsStrings + String_unicode); }
String *id_sticky() const { return reinterpret_cast<String *>(jsStrings + String_sticky); }
+ String *id_source() const { return reinterpret_cast<String *>(jsStrings + String_source); }
+ String *id_flags() const { return reinterpret_cast<String *>(jsStrings + String_flags); }
Symbol *symbol_hasInstance() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_hasInstance); }
Symbol *symbol_isConcatSpreadable() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_isConcatSpreadable); }
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 0acefc26d2..d0668d1ee4 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -165,8 +165,7 @@ QString RegExpObject::toString() const
QString RegExpObject::source() const
{
Scope scope(engine());
- ScopedString source(scope, scope.engine->newIdentifier(QStringLiteral("source")));
- ScopedValue s(scope, const_cast<RegExpObject *>(this)->get(source));
+ ScopedValue s(scope, get(scope.engine->id_source()));
return s->toQString();
}
@@ -244,31 +243,26 @@ void Heap::RegExpCtor::clearLastMatch()
lastMatchEnd = 0;
}
-ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *)
+static bool isRegExp(ExecutionEngine *e, const Value *arg)
{
- Scope scope(fo->engine());
- ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue());
- ScopedValue f(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
- Scoped<RegExpObject> re(scope, r);
- if (re) {
- if (!f->isUndefined())
- return scope.engine->throwTypeError();
-
- Scoped<RegExp> regexp(scope, re->value());
- return Encode(scope.engine->newRegExpObject(regexp));
- }
+ const Object *o = arg->objectValue();
+ if (!o)
+ return false;
- QString pattern;
- if (!r->isUndefined())
- pattern = r->toQString();
- if (scope.hasException())
- return Encode::undefined();
+ Value isRegExp = Primitive::fromReturnedValue(o->get(e->symbol_match()));
+ if (!isRegExp.isUndefined())
+ return isRegExp.toBoolean();
+ const RegExpObject *re = o->as<RegExpObject>();
+ return re ? true : false;
+}
+uint parseFlags(Scope &scope, const Value *f)
+{
uint flags = CompiledData::RegExp::RegExp_NoFlags;
if (!f->isUndefined()) {
ScopedString s(scope, f->toString(scope.engine));
if (scope.hasException())
- return Encode::undefined();
+ return flags;
QString str = s->toQString();
for (int i = 0; i < str.length(); ++i) {
if (str.at(i) == QLatin1Char('g') && !(flags & CompiledData::RegExp::RegExp_Global)) {
@@ -282,10 +276,61 @@ ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, con
} 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"));
+ scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
+ return flags;
}
}
}
+ return flags;
+}
+
+ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget)
+{
+ Scope scope(fo);
+
+ bool patternIsRegExp = argc ? ::isRegExp(scope.engine, argv) : false;
+
+ if (newTarget == fo) {
+ if (patternIsRegExp && (argc < 2 || argv[1].isUndefined())) {
+ const Object *pattern = static_cast<const Object *>(argv);
+ ScopedValue patternConstructor(scope, pattern->get(scope.engine->id_constructor()));
+ if (patternConstructor->sameValue(*newTarget))
+ return pattern->asReturnedValue();
+ }
+ }
+
+ ScopedValue p(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue f(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ Scoped<RegExpObject> re(scope, p);
+ QString pattern;
+ uint flags = CompiledData::RegExp::RegExp_NoFlags;
+
+ if (re) {
+ if (f->isUndefined()) {
+ Scoped<RegExp> regexp(scope, re->value());
+ return Encode(scope.engine->newRegExpObject(regexp));
+ }
+ pattern = *re->value()->pattern;
+ flags = parseFlags(scope, f);
+ } else if (patternIsRegExp) {
+ const Object *po = static_cast<const Object *>(argv);
+ p = po->get(scope.engine->id_source());
+ if (!p->isUndefined())
+ pattern = p->toQString();
+ if (scope.hasException())
+ return Encode::undefined();
+ if (f->isUndefined())
+ f = po->get(scope.engine->id_flags());
+ flags = parseFlags(scope, f);
+ } else {
+ if (!p->isUndefined())
+ pattern = p->toQString();
+ if (scope.hasException())
+ return Encode::undefined();
+ flags = parseFlags(scope, f);
+ }
+ if (scope.hasException())
+ return Encode::undefined();
Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, flags));
if (!regexp->isValid()) {
@@ -297,11 +342,6 @@ ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, con
ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- if (argc > 0 && argv[0].as<RegExpObject>()) {
- if (argc == 1 || argv[1].isUndefined())
- return Encode(argv[0]);
- }
-
return virtualCallAsConstructor(f, argv, argc, f);
}
@@ -337,7 +377,7 @@ 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(scope.engine->id_flags(), method_get_flags, nullptr);
defineAccessorProperty(scope.engine->id_global(), method_get_global, nullptr);
defineAccessorProperty(scope.engine->id_ignoreCase(), method_get_ignoreCase, nullptr);
defineDefaultProperty(QStringLiteral("exec"), method_exec, 1);
@@ -345,7 +385,7 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
defineAccessorProperty(scope.engine->id_multiline(), method_get_multiline, nullptr);
defineDefaultProperty(engine->symbol_replace(), method_replace, 2);
defineDefaultProperty(engine->symbol_search(), method_search, 1);
- defineAccessorProperty(QStringLiteral("source"), method_get_source, nullptr);
+ defineAccessorProperty(scope.engine->id_source(), method_get_source, nullptr);
defineAccessorProperty(scope.engine->id_sticky(), method_get_sticky, nullptr);
defineDefaultProperty(QStringLiteral("test"), method_test, 1);
defineDefaultProperty(engine->id_toString(), method_toString, 0);
@@ -751,13 +791,12 @@ ReturnedValue RegExpPrototype::method_toString(const FunctionObject *b, const Va
if (!r)
return scope.engine->throwTypeError();
- ScopedString key(scope);
ScopedValue v(scope);
- v = r->get((key = scope.engine->newString(QStringLiteral("source"))));
+ v = r->get(scope.engine->id_source());
ScopedString source(scope, v->toString(scope.engine));
if (scope.hasException())
return Encode::undefined();
- v = r->get((key = scope.engine->newString(QStringLiteral("flags"))));
+ v = r->get(scope.engine->id_flags());
ScopedString flags(scope, v->toString(scope.engine));
if (scope.hasException())
return Encode::undefined();