aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2018-08-10 09:22:18 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-15 14:24:48 +0000
commit552ea3d320542af09a1983ec9a9fd9ba4324abc9 (patch)
tree31769d3815668ded6b04c7788d49a4baa6c7510e
parent5819700e0c2e17f8aa3e02bbfdf1d760906d02a4 (diff)
Implement RegExp.prototype[Symbol.split]
Change-Id: Ia5ed7afc67122f4d70bf2e0169537f936df036a9 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp143
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h11
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations52
3 files changed, 137 insertions, 69 deletions
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index d0668d1ee4..dd33a9fc41 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -169,11 +169,6 @@ QString RegExpObject::source() const
return s->toQString();
}
-uint RegExpObject::flags() const
-{
- return d()->value->flags;
-}
-
ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *str)
{
QString s = str->toQString();
@@ -386,6 +381,7 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
defineDefaultProperty(engine->symbol_replace(), method_replace, 2);
defineDefaultProperty(engine->symbol_search(), method_search, 1);
defineAccessorProperty(scope.engine->id_source(), method_get_source, nullptr);
+ defineDefaultProperty(engine->symbol_split(), method_split, 2);
defineAccessorProperty(scope.engine->id_sticky(), method_get_sticky, nullptr);
defineDefaultProperty(QStringLiteral("test"), method_test, 1);
defineDefaultProperty(engine->id_toString(), method_toString, 0);
@@ -538,19 +534,24 @@ ReturnedValue RegExpPrototype::method_get_ignoreCase(const FunctionObject *f, co
return Encode(b);
}
+static int advanceStringIndex(int index, const QString &str, bool unicode)
+{
+ if (unicode) {
+ if (index < str.length() - 1 &&
+ str.at(index).isHighSurrogate() &&
+ str.at(index + 1).isLowSurrogate())
+ ++index;
+ }
+ ++index;
+ return index;
+}
+
static void advanceLastIndexOnEmptyMatch(ExecutionEngine *e, bool unicode, Object *rx, const String *matchString, const QString &str)
{
Scope scope(e);
if (matchString->d()->length() == 0) {
ScopedValue v(scope, rx->get(scope.engine->id_lastIndex()));
- int lastIndex = v->toLength();
- if (unicode) {
- if (lastIndex < str.length() - 1 &&
- str.at(lastIndex).isHighSurrogate() &&
- str.at(lastIndex + 1).isLowSurrogate())
- ++lastIndex;
- }
- ++lastIndex;
+ int lastIndex = advanceStringIndex(v->toLength(), str, unicode);
if (!rx->put(scope.engine->id_lastIndex(), Primitive::fromInt32(lastIndex)))
scope.engine->throwTypeError();
}
@@ -764,6 +765,122 @@ ReturnedValue RegExpPrototype::method_get_source(const FunctionObject *f, const
return scope.engine->newString(re->toString())->asReturnedValue();
}
+static const FunctionObject *speciesConstructor(Scope &scope, const Object *o, const FunctionObject *defaultConstructor)
+{
+ ScopedValue C(scope, o->get(scope.engine->id_constructor()));
+ if (C->isUndefined())
+ return defaultConstructor;
+ const Object *c = C->objectValue();
+ if (!c) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ ScopedValue S(scope, c->get(scope.engine->symbol_species()));
+ if (S->isNullOrUndefined())
+ return defaultConstructor;
+ if (!S->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ return static_cast<const FunctionObject *>(S.ptr);
+}
+
+ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedObject rx(scope, thisObject);
+ if (!rx)
+ return scope.engine->throwTypeError();
+
+ ScopedString s(scope, (argc ? argv[0] : Primitive::undefinedValue()).toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ScopedValue flagsValue(scope, rx->get(scope.engine->id_flags()));
+ ScopedString flags(scope, flagsValue->toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ QString flagsString = flags->toQString();
+ if (!flagsString.contains(QLatin1Char('y')))
+ flags = scope.engine->newString(flagsString + QLatin1Char('y'));
+ bool unicodeMatching = flagsString.contains(QLatin1Char('u'));
+
+ const FunctionObject *C = speciesConstructor(scope, rx, scope.engine->regExpCtor());
+ if (!C)
+ return Encode::undefined();
+
+ Value *args = scope.alloc(2);
+ args[0] = rx;
+ args[1] = flags;
+ ScopedObject splitter(scope, C->callAsConstructor(args, 2, f));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ScopedArrayObject A(scope, scope.engine->newArrayObject());
+ uint lengthA = 0;
+ uint limit = argc < 2 ? UINT_MAX : argv[1].toUInt32();
+ if (limit == 0)
+ return A->asReturnedValue();
+
+ QString S = s->toQString();
+ int size = S.length();
+ if (size == 0) {
+ ScopedValue z(scope, exec(scope.engine, splitter, s));
+ if (z->isNull())
+ A->push_back(s);
+ return A->asReturnedValue();
+ }
+
+ int p = 0;
+ int q = 0;
+ ScopedValue v(scope);
+ ScopedValue z(scope);
+ ScopedObject zz(scope);
+ ScopedString t(scope);
+ while (q < size) {
+ Value qq = Primitive::fromInt32(q);
+ if (!splitter->put(scope.engine->id_lastIndex(), qq))
+ return scope.engine->throwTypeError();
+ z = exec(scope.engine, splitter, s);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ if (z->isNull()) {
+ q = advanceStringIndex(q, S, unicodeMatching);
+ continue;
+ }
+
+ v = splitter->get(scope.engine->id_lastIndex());
+ int e = qMin(v->toInt32(), size);
+ if (e == p) {
+ q = advanceStringIndex(q, S, unicodeMatching);
+ continue;
+ }
+ QString T = S.mid(p, q - p);
+ t = scope.engine->newString(T);
+ A->push_back(t);
+ ++lengthA;
+ if (lengthA == limit)
+ return A->asReturnedValue();
+ p = e;
+ zz = *z;
+ uint numberOfCaptures = qMax(zz->getLength() - 1, 0ll);
+ for (uint i = 1; i <= numberOfCaptures; ++i) {
+ v = zz->get(PropertyKey::fromArrayIndex(i));
+ A->push_back(v);
+ ++lengthA;
+ if (lengthA == limit)
+ return A->asReturnedValue();
+ }
+ q = p;
+ }
+
+ QString T = S.mid(p);
+ t = scope.engine->newString(T);
+ A->push_back(t);
+ return A->asReturnedValue();
+}
+
ReturnedValue RegExpPrototype::method_get_sticky(const FunctionObject *f, const Value *thisObject, const Value *, int)
{
Scope scope(f);
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index ba5a106c6b..ffac04e1f7 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -121,9 +121,6 @@ struct RegExpObject: Object {
enum { NInlineProperties = 5 };
- Heap::RegExp *value() const { return d()->value; }
- bool global() const { return d()->value->global(); }
- bool sticky() const { return d()->value->sticky(); }
void initProperties();
@@ -143,7 +140,12 @@ struct RegExpObject: Object {
QRegExp toQRegExp() const;
QString toString() const;
QString source() const;
- uint flags() const;
+
+ Heap::RegExp *value() const { return d()->value; }
+ uint flags() const { return d()->value->flags; }
+ bool global() const { return d()->value->global(); }
+ bool sticky() const { return d()->value->sticky(); }
+ bool unicode() const { return d()->value->unicode(); }
ReturnedValue builtinExec(ExecutionEngine *engine, const String *s);
};
@@ -174,6 +176,7 @@ struct RegExpPrototype: Object
static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_search(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_split(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);
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index cacd6f3787..e220f86f17 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -693,63 +693,11 @@ built-ins/Reflect/set/receiver-is-not-object.js fails
built-ins/Reflect/set/return-false-if-target-is-not-writable.js fails
built-ins/Reflect/set/set-value-on-data-descriptor.js fails
built-ins/Reflect/set/symbol-property.js fails
-built-ins/RegExp/15.10.4.1-1.js fails
built-ins/RegExp/S15.10.2.12_A2_T1.js fails
-built-ins/RegExp/S15.10.3.1_A2_T1.js fails
-built-ins/RegExp/S15.10.3.1_A2_T2.js fails
-built-ins/RegExp/S15.10.4.1_A2_T1.js fails
-built-ins/RegExp/S15.10.4.1_A2_T2.js fails
-built-ins/RegExp/call_with_non_regexp_same_constructor.js fails
-built-ins/RegExp/call_with_regexp_match_falsy.js fails
-built-ins/RegExp/call_with_regexp_not_same_constructor.js fails
-built-ins/RegExp/from-regexp-like-flag-override.js fails
-built-ins/RegExp/from-regexp-like-get-source-err.js fails
-built-ins/RegExp/from-regexp-like-short-circuit.js fails
-built-ins/RegExp/from-regexp-like.js fails
built-ins/RegExp/proto-from-ctor-realm.js fails
built-ins/RegExp/prototype/Symbol.match/builtin-success-u-return-val-groups.js fails
-built-ins/RegExp/prototype/Symbol.split/coerce-flags-err.js fails
-built-ins/RegExp/prototype/Symbol.split/coerce-flags.js fails
-built-ins/RegExp/prototype/Symbol.split/coerce-limit-err.js fails
-built-ins/RegExp/prototype/Symbol.split/coerce-limit.js fails
-built-ins/RegExp/prototype/Symbol.split/coerce-string-err.js fails
-built-ins/RegExp/prototype/Symbol.split/coerce-string.js fails
-built-ins/RegExp/prototype/Symbol.split/get-flags-err.js fails
-built-ins/RegExp/prototype/Symbol.split/last-index-exceeds-str-size.js fails
-built-ins/RegExp/prototype/Symbol.split/length.js fails
-built-ins/RegExp/prototype/Symbol.split/limit-0-bail.js fails
-built-ins/RegExp/prototype/Symbol.split/name.js fails
-built-ins/RegExp/prototype/Symbol.split/prop-desc.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-ctor-get-err.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-ctor-non-obj.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-ctor-undef.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-err.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-species-get-err.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-species-non-ctor.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-species-undef.js fails
-built-ins/RegExp/prototype/Symbol.split/species-ctor-y.js fails
built-ins/RegExp/prototype/Symbol.split/species-ctor.js fails
built-ins/RegExp/prototype/Symbol.split/splitter-proto-from-ctor-realm.js fails
-built-ins/RegExp/prototype/Symbol.split/str-adv-thru-empty-match.js fails
-built-ins/RegExp/prototype/Symbol.split/str-coerce-lastindex-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-coerce-lastindex.js fails
-built-ins/RegExp/prototype/Symbol.split/str-empty-match-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-empty-match.js fails
-built-ins/RegExp/prototype/Symbol.split/str-empty-no-match.js fails
-built-ins/RegExp/prototype/Symbol.split/str-get-lastindex-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-limit-capturing.js fails
-built-ins/RegExp/prototype/Symbol.split/str-limit.js fails
-built-ins/RegExp/prototype/Symbol.split/str-match-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-result-coerce-length-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-result-coerce-length.js fails
-built-ins/RegExp/prototype/Symbol.split/str-result-get-capture-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-result-get-length-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-set-lastindex-err.js fails
-built-ins/RegExp/prototype/Symbol.split/str-set-lastindex-match.js fails
-built-ins/RegExp/prototype/Symbol.split/str-set-lastindex-no-match.js fails
-built-ins/RegExp/prototype/Symbol.split/str-trailing-chars.js fails
-built-ins/RegExp/prototype/Symbol.split/u-lastindex-adv-thru-failure.js fails
-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