141ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine,
const String *str)
143 QString s = str->toQString();
146 int offset = (global() || sticky()) ? lastIndex() : 0;
147 if (offset < 0 || offset > s.size()) {
152 Q_ALLOCA_VAR(uint, matchOffsets, value()->captureCount() * 2 *
sizeof(uint));
153 const uint result = Scoped<RegExp>(scope, value())->match(s, offset, matchOffsets);
155 RegExpCtor *regExpCtor =
static_cast<RegExpCtor *>(scope.engine->regExpCtor());
156 regExpCtor->d()->clearLastMatch();
158 if (result == JSC::Yarr::offsetNoMatch) {
159 if (global() || sticky())
164 Q_ASSERT(result <= uint(std::numeric_limits<
int>::max()));
167 ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray)));
168 int len = value()->captureCount();
169 array->arrayReserve(len);
170 ScopedValue v(scope);
171 int strlen = s.size();
172 for (
int i = 0; i < len; ++i) {
173 int start = matchOffsets[i * 2];
174 int end = matchOffsets[i * 2 + 1];
177 v = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
178 array->arrayPut(i, v);
180 array->setArrayLengthUnchecked(len);
181 array->setProperty(Index_ArrayIndex, Value::fromInt32(
int(result)));
182 array->setProperty(Index_ArrayInput, *str);
184 RegExpCtor::Data *dd = regExpCtor->d();
185 dd->lastMatch.set(scope.engine, array);
186 dd->lastInput.set(scope.engine, str->d());
187 dd->lastMatchStart = matchOffsets[0];
188 dd->lastMatchEnd = matchOffsets[1];
190 if (global() || sticky())
191 setLastIndex(matchOffsets[1]);
193 return array.asReturnedValue();
227 CompiledData::RegExp::Flags flags = CompiledData::RegExp::RegExp_NoFlags;
228 if (!f->isUndefined()) {
229 ScopedString s(scope, f->toString(scope.engine));
230 if (scope.hasException())
232 QString str = s->toQString();
233 for (
int i = 0; i < str.size(); ++i) {
234 if (str.at(i) == QLatin1Char(
'g') && !(flags & CompiledData::RegExp::RegExp_Global)) {
235 flags |= CompiledData::RegExp::RegExp_Global;
236 }
else if (str.at(i) == QLatin1Char(
'i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) {
237 flags |= CompiledData::RegExp::RegExp_IgnoreCase;
238 }
else if (str.at(i) == QLatin1Char(
'm') && !(flags & CompiledData::RegExp::RegExp_Multiline)) {
239 flags |= CompiledData::RegExp::RegExp_Multiline;
240 }
else if (str.at(i) == QLatin1Char(
'u') && !(flags & CompiledData::RegExp::RegExp_Unicode)) {
241 flags |= CompiledData::RegExp::RegExp_Unicode;
242 }
else if (str.at(i) == QLatin1Char(
'y') && !(flags & CompiledData::RegExp::RegExp_Sticky)) {
243 flags |= CompiledData::RegExp::RegExp_Sticky;
245 scope.engine->throwSyntaxError(QStringLiteral(
"Invalid flags supplied to RegExp constructor"));
253ReturnedValue RegExpCtor::virtualCallAsConstructor(
const FunctionObject *fo,
const Value *argv,
int argc,
const Value *newTarget)
257 bool patternIsRegExp = argc ? ::isRegExp(scope.engine, argv) :
false;
259 if (newTarget == fo) {
260 if (patternIsRegExp && (argc < 2 || argv[1].isUndefined())) {
261 const Object *pattern =
static_cast<
const Object *>(argv);
262 ScopedValue patternConstructor(scope, pattern->get(scope.engine->id_constructor()));
263 if (patternConstructor->sameValue(*newTarget))
264 return pattern->asReturnedValue();
268 ScopedValue p(scope, argc ? argv[0] : Value::undefinedValue());
269 ScopedValue f(scope, argc > 1 ? argv[1] : Value::undefinedValue());
270 Scoped<RegExpObject> re(scope, p);
272 CompiledData::RegExp::Flags flags = CompiledData::RegExp::RegExp_NoFlags;
275 if (f->isUndefined()) {
276 Scoped<RegExp> regexp(scope, re->value());
277 return Encode(scope.engine->newRegExpObject(regexp));
279 pattern = *re->value()->pattern;
280 flags = parseFlags(scope, f);
281 }
else if (patternIsRegExp) {
282 const Object *po =
static_cast<
const Object *>(argv);
283 p = po->get(scope.engine->id_source());
284 if (!p->isUndefined())
285 pattern = p->toQString();
286 if (scope.hasException())
287 return Encode::undefined();
288 if (f->isUndefined())
289 f = po->get(scope.engine->id_flags());
290 flags = parseFlags(scope, f);
292 if (!p->isUndefined())
293 pattern = p->toQString();
294 if (scope.hasException())
295 return Encode::undefined();
296 flags = parseFlags(scope, f);
298 if (scope.hasException())
299 return Encode::undefined();
301 Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, flags));
302 if (!regexp->isValid()) {
303 return scope.engine->throwSyntaxError(QStringLiteral(
"Invalid regular expression"));
306 ReturnedValue o = Encode(scope.engine->newRegExpObject(regexp));
310 ScopedObject obj(scope, o);
311 obj->setProtoFromNewTarget(newTarget);
312 return obj->asReturnedValue();
320void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
323 ScopedObject o(scope);
324 ScopedObject ctor(scope, constructor);
326 ctor->defineReadonlyProperty(engine->id_prototype(), (o =
this));
327 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(2));
328 ctor->addSymbolSpecies();
331 ctor->defineAccessorProperty(QStringLiteral(
"lastMatch"), method_get_lastMatch_n<0>,
nullptr);
332 ctor->defineAccessorProperty(QStringLiteral(
"$&"), method_get_lastMatch_n<0>,
nullptr);
333 ctor->defineAccessorProperty(QStringLiteral(
"$1"), method_get_lastMatch_n<1>,
nullptr);
334 ctor->defineAccessorProperty(QStringLiteral(
"$2"), method_get_lastMatch_n<2>,
nullptr);
335 ctor->defineAccessorProperty(QStringLiteral(
"$3"), method_get_lastMatch_n<3>,
nullptr);
336 ctor->defineAccessorProperty(QStringLiteral(
"$4"), method_get_lastMatch_n<4>,
nullptr);
337 ctor->defineAccessorProperty(QStringLiteral(
"$5"), method_get_lastMatch_n<5>,
nullptr);
338 ctor->defineAccessorProperty(QStringLiteral(
"$6"), method_get_lastMatch_n<6>,
nullptr);
339 ctor->defineAccessorProperty(QStringLiteral(
"$7"), method_get_lastMatch_n<7>,
nullptr);
340 ctor->defineAccessorProperty(QStringLiteral(
"$8"), method_get_lastMatch_n<8>,
nullptr);
341 ctor->defineAccessorProperty(QStringLiteral(
"$9"), method_get_lastMatch_n<9>,
nullptr);
342 ctor->defineAccessorProperty(QStringLiteral(
"lastParen"), method_get_lastParen,
nullptr);
343 ctor->defineAccessorProperty(QStringLiteral(
"$+"), method_get_lastParen,
nullptr);
344 ctor->defineAccessorProperty(QStringLiteral(
"input"), method_get_input,
nullptr);
345 ctor->defineAccessorProperty(QStringLiteral(
"$_"), method_get_input,
nullptr);
346 ctor->defineAccessorProperty(QStringLiteral(
"leftContext"), method_get_leftContext,
nullptr);
347 ctor->defineAccessorProperty(QStringLiteral(
"$`"), method_get_leftContext,
nullptr);
348 ctor->defineAccessorProperty(QStringLiteral(
"rightContext"), method_get_rightContext,
nullptr);
349 ctor->defineAccessorProperty(QStringLiteral(
"$'"), method_get_rightContext,
nullptr);
351 defineDefaultProperty(QStringLiteral(
"constructor"), (o = ctor));
352 defineAccessorProperty(scope.engine->id_flags(), method_get_flags,
nullptr);
353 defineAccessorProperty(scope.engine->id_global(), method_get_global,
nullptr);
354 defineAccessorProperty(scope.engine->id_ignoreCase(), method_get_ignoreCase,
nullptr);
355 defineDefaultProperty(QStringLiteral(
"exec"), method_exec, 1);
356 defineDefaultProperty(engine->symbol_match(), method_match, 1);
357 defineAccessorProperty(scope.engine->id_multiline(), method_get_multiline,
nullptr);
358 defineDefaultProperty(engine->symbol_replace(), method_replace, 2);
359 defineDefaultProperty(engine->symbol_search(), method_search, 1);
360 defineAccessorProperty(scope.engine->id_source(), method_get_source,
nullptr);
361 defineDefaultProperty(engine->symbol_split(), method_split, 2);
362 defineAccessorProperty(scope.engine->id_sticky(), method_get_sticky,
nullptr);
363 defineDefaultProperty(QStringLiteral(
"test"), method_test, 1);
364 defineDefaultProperty(engine->id_toString(), method_toString, 0);
365 defineAccessorProperty(scope.engine->id_unicode(), method_get_unicode,
nullptr);
368 defineDefaultProperty(QStringLiteral(
"compile"), method_compile, 2);
372ReturnedValue RegExpPrototype::execFirstMatch(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
375 Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
376 Q_ASSERT(r && r->global());
378 ScopedString str(scope, argc ? argv[0] : Value::undefinedValue());
380 QString s = str->toQString();
382 int offset = r->lastIndex();
383 if (offset < 0 || offset > s.size()) {
388 Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 *
sizeof(uint));
389 const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets);
391 RegExpCtor *regExpCtor =
static_cast<RegExpCtor *>(scope.engine->regExpCtor());
392 regExpCtor->d()->clearLastMatch();
399 ReturnedValue retVal = Encode::undefined();
401 if (r->value()->captureCount()) {
402 int start = matchOffsets[0];
403 int end = matchOffsets[1];
404 retVal = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
407 RegExpCtor::Data *dd = regExpCtor->d();
408 dd->lastInput.set(scope.engine, str->d());
409 dd->lastMatchStart = matchOffsets[0];
410 dd->lastMatchEnd = matchOffsets[1];
412 r->setLastIndex(matchOffsets[1]);
417ReturnedValue RegExpPrototype::exec(ExecutionEngine *engine,
const Object *o,
const String *s)
420 ScopedString key(scope, scope.engine->newString(QStringLiteral(
"exec")));
421 ScopedFunctionObject exec(scope, o->get(key));
423 ScopedValue result(scope, exec->call(o, s, 1));
424 if (scope.hasException())
426 if (!result->isNull() && !result->isObject())
427 return scope.engine->throwTypeError();
428 return result->asReturnedValue();
430 Scoped<RegExpObject> re(scope, o);
432 return scope.engine->throwTypeError();
433 return re->builtinExec(engine, s);
539ReturnedValue RegExpPrototype::method_match(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
542 ScopedObject rx(scope, thisObject);
544 return scope.engine->throwTypeError();
545 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
546 if (scope.hasException())
547 return Encode::undefined();
548 bool global = ScopedValue(scope, rx->get(scope.engine->id_global()))->toBoolean();
551 return exec(scope.engine, rx, s);
553 bool unicode = ScopedValue(scope, rx->get(scope.engine->id_unicode()))->toBoolean();
555 rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0));
556 ScopedArrayObject a(scope, scope.engine->newArrayObject());
559 ScopedValue result(scope);
560 ScopedValue match(scope);
561 ScopedString matchString(scope);
562 ScopedValue v(scope);
564 result = exec(scope.engine, rx, s);
565 if (scope.hasException())
566 return Encode::undefined();
567 if (result->isNull()) {
569 return Encode::null();
570 return a->asReturnedValue();
572 Q_ASSERT(result->isObject());
573 match =
static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
574 matchString = match->toString(scope.engine);
575 if (scope.hasException())
576 return Encode::undefined();
577 a->push_back(matchString);
578 advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
597ReturnedValue RegExpPrototype::method_replace(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
600 ScopedObject rx(scope, thisObject);
602 return scope.engine->throwTypeError();
604 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
605 if (scope.hasException())
606 return Encode::undefined();
608 int lengthS = s->toQString().size();
610 ScopedString replaceValue(scope);
611 ScopedFunctionObject replaceFunction(scope, (argc > 1 ? argv[1] : Value::undefinedValue()));
612 bool functionalReplace = !!replaceFunction;
613 if (!functionalReplace)
614 replaceValue = (argc > 1 ? argv[1] : Value::undefinedValue()).toString(scope.engine);
616 ScopedValue v(scope);
617 bool global = (v = rx->get(scope.engine->id_global()))->toBoolean();
618 bool unicode =
false;
620 unicode = (v = rx->get(scope.engine->id_unicode()))->toBoolean();
621 if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
622 return scope.engine->throwTypeError();
625 ScopedArrayObject results(scope, scope.engine->newArrayObject());
626 ScopedValue result(scope);
627 ScopedValue match(scope);
628 ScopedString matchString(scope);
630 result = exec(scope.engine, rx, s);
631 if (scope.hasException())
632 return Encode::undefined();
633 if (result->isNull())
635 results->push_back(result);
638 match =
static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
639 matchString = match->toString(scope.engine);
640 if (scope.hasException())
641 return Encode::undefined();
642 advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
644 QString accumulatedResult;
645 int nextSourcePosition = 0;
646 int resultsLength = results->getLength();
647 ScopedObject resultObject(scope);
648 for (
int i = 0; i < resultsLength; ++i) {
649 resultObject = results->get(PropertyKey::fromArrayIndex(i));
650 if (scope.hasException())
651 return Encode::undefined();
653 int nCaptures = resultObject->getLength();
654 nCaptures = qMax(nCaptures - 1, 0);
655 match = resultObject->get(PropertyKey::fromArrayIndex(0));
656 matchString = match->toString(scope.engine);
657 if (scope.hasException())
658 return Encode::undefined();
659 QString m = matchString->toQString();
660 int matchLength = m.size();
661 v = resultObject->get(scope.engine->id_index());
662 int position = v->toInt32();
663 position = qMax(qMin(position, lengthS), 0);
664 if (scope.hasException())
665 return Encode::undefined();
668 Scope innerScope(scope.engine);
669 JSCallArguments cData(scope, nCaptures + 3);
670 while (n <= nCaptures) {
671 v = resultObject->get(PropertyKey::fromArrayIndex(n));
672 if (!v->isUndefined())
673 cData.args[n] = v->toString(scope.engine);
677 if (functionalReplace) {
678 cData.args[0] = matchString;
679 cData.args[nCaptures + 1] = Encode(position);
680 cData.args[nCaptures + 2] = s;
681 ScopedValue replValue(scope, replaceFunction->call(cData));
682 if (scope.hasException())
683 return Encode::undefined();
684 replacement = replValue->toQString();
686 replacement = RegExp::getSubstitution(matchString->toQString(), s->toQString(), position, cData.args, nCaptures, replaceValue->toQString());
688 if (scope.hasException())
689 return Encode::undefined();
690 if (position >= nextSourcePosition) {
691 accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition, position - nextSourcePosition) + replacement;
692 nextSourcePosition = position + matchLength;
695 if (nextSourcePosition < lengthS) {
696 accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition);
698 return scope.engine->newString(accumulatedResult)->asReturnedValue();
701ReturnedValue RegExpPrototype::method_search(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
704 ScopedObject rx(scope, thisObject);
706 return scope.engine->throwTypeError();
708 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
709 if (scope.hasException())
710 return Encode::undefined();
712 ScopedValue previousLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
713 if (previousLastIndex->toNumber() != 0) {
714 if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
715 return scope.engine->throwTypeError();
718 ScopedValue result(scope, exec(scope.engine, rx, s));
719 if (scope.hasException())
720 return Encode::undefined();
722 ScopedValue currentLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
723 if (!currentLastIndex->sameValue(previousLastIndex)) {
724 if (!rx->put(scope.engine->id_lastIndex(), previousLastIndex))
725 return scope.engine->throwTypeError();
728 if (result->isNull())
730 ScopedObject o(scope, result);
732 return o->get(scope.engine->id_index());
749ReturnedValue RegExpPrototype::method_split(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
752 ScopedObject rx(scope, thisObject);
754 return scope.engine->throwTypeError();
756 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
757 if (scope.hasException())
758 return Encode::undefined();
760 ScopedValue flagsValue(scope, rx->get(scope.engine->id_flags()));
761 ScopedString flags(scope, flagsValue->toString(scope.engine));
762 if (scope.hasException())
763 return Encode::undefined();
764 QString flagsString = flags->toQString();
765 if (!flagsString.contains(QLatin1Char(
'y')))
766 flags = scope.engine->newString(flagsString + QLatin1Char(
'y'));
767 bool unicodeMatching = flagsString.contains(QLatin1Char(
'u'));
769 const FunctionObject *C = rx->speciesConstructor(scope, scope.engine->regExpCtor());
771 return Encode::undefined();
773 Value *args = scope.alloc(2);
776 ScopedObject splitter(scope, C->callAsConstructor(args, 2, f));
777 if (scope.hasException())
778 return Encode::undefined();
780 ScopedArrayObject A(scope, scope.engine->newArrayObject());
782 uint limit = argc < 2 ? UINT_MAX : argv[1].toUInt32();
784 return A->asReturnedValue();
786 QString S = s->toQString();
789 ScopedValue z(scope, exec(scope.engine, splitter, s));
792 return A->asReturnedValue();
797 ScopedValue v(scope);
798 ScopedValue z(scope);
799 ScopedObject zz(scope);
800 ScopedString t(scope);
802 Value qq = Value::fromInt32(q);
803 if (!splitter->put(scope.engine->id_lastIndex(), qq))
804 return scope.engine->throwTypeError();
805 z = exec(scope.engine, splitter, s);
806 if (scope.hasException())
807 return Encode::undefined();
810 q = advanceStringIndex(q, S, unicodeMatching);
814 v = splitter->get(scope.engine->id_lastIndex());
815 int e = qMin(v->toInt32(), size);
817 q = advanceStringIndex(q, S, unicodeMatching);
820 QString T = S.mid(p, q - p);
821 t = scope.engine->newString(T);
824 if (lengthA == limit)
825 return A->asReturnedValue();
828 uint numberOfCaptures = qMax(zz->getLength() - 1, 0ll);
829 for (uint i = 1; i <= numberOfCaptures; ++i) {
830 v = zz->get(PropertyKey::fromArrayIndex(i));
833 if (lengthA == limit)
834 return A->asReturnedValue();
839 QString T = S.mid(p);
840 t = scope.engine->newString(T);
842 return A->asReturnedValue();