142ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine,
const String *str)
144 QString s = str->toQString();
147 int offset = (global() || sticky()) ? lastIndex() : 0;
148 if (offset < 0 || offset > s.size()) {
153 Q_ALLOCA_VAR(uint, matchOffsets, value()->captureCount() * 2 *
sizeof(uint));
154 const uint result = Scoped<RegExp>(scope, value())->match(s, offset, matchOffsets);
156 RegExpCtor *regExpCtor =
static_cast<RegExpCtor *>(scope.engine->regExpCtor());
157 regExpCtor->d()->clearLastMatch();
159 if (result == JSC::Yarr::offsetNoMatch) {
160 if (global() || sticky())
165 Q_ASSERT(result <= uint(std::numeric_limits<
int>::max()));
168 ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray)));
169 int len = value()->captureCount();
170 array->arrayReserve(len);
171 ScopedValue v(scope);
172 int strlen = s.size();
173 for (
int i = 0; i < len; ++i) {
174 int start = matchOffsets[i * 2];
175 int end = matchOffsets[i * 2 + 1];
178 v = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
179 array->arrayPut(i, v);
181 array->setArrayLengthUnchecked(len);
182 array->setProperty(Index_ArrayIndex, Value::fromInt32(
int(result)));
183 array->setProperty(Index_ArrayInput, *str);
185 RegExpCtor::Data *dd = regExpCtor->d();
186 dd->lastMatch.set(scope.engine, array);
187 dd->lastInput.set(scope.engine, str->d());
188 dd->lastMatchStart = matchOffsets[0];
189 dd->lastMatchEnd = matchOffsets[1];
191 if (global() || sticky())
192 setLastIndex(matchOffsets[1]);
194 return array.asReturnedValue();
228 CompiledData::RegExp::Flags flags = CompiledData::RegExp::RegExp_NoFlags;
229 if (!f->isUndefined()) {
230 ScopedString s(scope, f->toString(scope.engine));
231 if (scope.hasException())
233 QString str = s->toQString();
234 for (
int i = 0; i < str.size(); ++i) {
235 if (str.at(i) == QLatin1Char(
'g') && !(flags & CompiledData::RegExp::RegExp_Global)) {
236 flags |= CompiledData::RegExp::RegExp_Global;
237 }
else if (str.at(i) == QLatin1Char(
'i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) {
238 flags |= CompiledData::RegExp::RegExp_IgnoreCase;
239 }
else if (str.at(i) == QLatin1Char(
'm') && !(flags & CompiledData::RegExp::RegExp_Multiline)) {
240 flags |= CompiledData::RegExp::RegExp_Multiline;
241 }
else if (str.at(i) == QLatin1Char(
'u') && !(flags & CompiledData::RegExp::RegExp_Unicode)) {
242 flags |= CompiledData::RegExp::RegExp_Unicode;
243 }
else if (str.at(i) == QLatin1Char(
'y') && !(flags & CompiledData::RegExp::RegExp_Sticky)) {
244 flags |= CompiledData::RegExp::RegExp_Sticky;
246 scope.engine->throwSyntaxError(QStringLiteral(
"Invalid flags supplied to RegExp constructor"));
254ReturnedValue
RegExpCtor::virtualCallAsConstructor(
const FunctionObject *fo,
const Value *argv,
int argc,
const Value *newTarget)
258 bool patternIsRegExp = argc ? ::isRegExp(scope.engine, argv) :
false;
260 if (newTarget == fo) {
261 if (patternIsRegExp && (argc < 2 || argv[1].isUndefined())) {
262 const Object *pattern =
static_cast<
const Object *>(argv);
263 ScopedValue patternConstructor(scope, pattern->get(scope.engine->id_constructor()));
264 if (patternConstructor->sameValue(*newTarget))
265 return pattern->asReturnedValue();
269 ScopedValue p(scope, argc ? argv[0] : Value::undefinedValue());
270 ScopedValue f(scope, argc > 1 ? argv[1] : Value::undefinedValue());
271 Scoped<RegExpObject> re(scope, p);
273 CompiledData::RegExp::Flags flags = CompiledData::RegExp::RegExp_NoFlags;
276 if (f->isUndefined()) {
277 Scoped<RegExp> regexp(scope, re->value());
278 return Encode(scope.engine->newRegExpObject(regexp));
280 pattern = *re->value()->pattern;
281 flags = parseFlags(scope, f);
282 }
else if (patternIsRegExp) {
283 const Object *po =
static_cast<
const Object *>(argv);
284 p = po->get(scope.engine->id_source());
285 if (!p->isUndefined())
286 pattern = p->toQString();
287 if (scope.hasException())
288 return Encode::undefined();
289 if (f->isUndefined())
290 f = po->get(scope.engine->id_flags());
291 flags = parseFlags(scope, f);
293 if (!p->isUndefined())
294 pattern = p->toQString();
295 if (scope.hasException())
296 return Encode::undefined();
297 flags = parseFlags(scope, f);
299 if (scope.hasException())
300 return Encode::undefined();
302 Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, flags));
303 if (!regexp->isValid()) {
304 return scope.engine->throwSyntaxError(QStringLiteral(
"Invalid regular expression"));
307 ReturnedValue o = Encode(scope.engine->newRegExpObject(regexp));
311 ScopedObject obj(scope, o);
312 obj->setProtoFromNewTarget(newTarget);
313 return obj->asReturnedValue();
324 ScopedObject o(scope);
325 ScopedObject ctor(scope, constructor);
327 ctor->defineReadonlyProperty(engine->id_prototype(), (o =
this));
328 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(2));
329 ctor->addSymbolSpecies();
332 ctor->defineAccessorProperty(QStringLiteral(
"lastMatch"), method_get_lastMatch_n<0>,
nullptr);
333 ctor->defineAccessorProperty(QStringLiteral(
"$&"), method_get_lastMatch_n<0>,
nullptr);
334 ctor->defineAccessorProperty(QStringLiteral(
"$1"), method_get_lastMatch_n<1>,
nullptr);
335 ctor->defineAccessorProperty(QStringLiteral(
"$2"), method_get_lastMatch_n<2>,
nullptr);
336 ctor->defineAccessorProperty(QStringLiteral(
"$3"), method_get_lastMatch_n<3>,
nullptr);
337 ctor->defineAccessorProperty(QStringLiteral(
"$4"), method_get_lastMatch_n<4>,
nullptr);
338 ctor->defineAccessorProperty(QStringLiteral(
"$5"), method_get_lastMatch_n<5>,
nullptr);
339 ctor->defineAccessorProperty(QStringLiteral(
"$6"), method_get_lastMatch_n<6>,
nullptr);
340 ctor->defineAccessorProperty(QStringLiteral(
"$7"), method_get_lastMatch_n<7>,
nullptr);
341 ctor->defineAccessorProperty(QStringLiteral(
"$8"), method_get_lastMatch_n<8>,
nullptr);
342 ctor->defineAccessorProperty(QStringLiteral(
"$9"), method_get_lastMatch_n<9>,
nullptr);
343 ctor->defineAccessorProperty(QStringLiteral(
"lastParen"), method_get_lastParen,
nullptr);
344 ctor->defineAccessorProperty(QStringLiteral(
"$+"), method_get_lastParen,
nullptr);
345 ctor->defineAccessorProperty(QStringLiteral(
"input"), method_get_input,
nullptr);
346 ctor->defineAccessorProperty(QStringLiteral(
"$_"), method_get_input,
nullptr);
347 ctor->defineAccessorProperty(QStringLiteral(
"leftContext"), method_get_leftContext,
nullptr);
348 ctor->defineAccessorProperty(QStringLiteral(
"$`"), method_get_leftContext,
nullptr);
349 ctor->defineAccessorProperty(QStringLiteral(
"rightContext"), method_get_rightContext,
nullptr);
350 ctor->defineAccessorProperty(QStringLiteral(
"$'"), method_get_rightContext,
nullptr);
352 defineDefaultProperty(QStringLiteral(
"constructor"), (o = ctor));
353 defineAccessorProperty(scope.engine->id_flags(), method_get_flags,
nullptr);
354 defineAccessorProperty(scope.engine->id_global(), method_get_global,
nullptr);
355 defineAccessorProperty(scope.engine->id_ignoreCase(), method_get_ignoreCase,
nullptr);
356 defineDefaultProperty(QStringLiteral(
"exec"), method_exec, 1);
357 defineDefaultProperty(engine->symbol_match(), method_match, 1);
358 defineAccessorProperty(scope.engine->id_multiline(), method_get_multiline,
nullptr);
359 defineDefaultProperty(engine->symbol_replace(), method_replace, 2);
360 defineDefaultProperty(engine->symbol_search(), method_search, 1);
361 defineAccessorProperty(scope.engine->id_source(), method_get_source,
nullptr);
362 defineDefaultProperty(engine->symbol_split(), method_split, 2);
363 defineAccessorProperty(scope.engine->id_sticky(), method_get_sticky,
nullptr);
364 defineDefaultProperty(QStringLiteral(
"test"), method_test, 1);
365 defineDefaultProperty(engine->id_toString(), method_toString, 0);
366 defineAccessorProperty(scope.engine->id_unicode(), method_get_unicode,
nullptr);
369 defineDefaultProperty(QStringLiteral(
"compile"), method_compile, 2);
373ReturnedValue
RegExpPrototype::execFirstMatch(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
376 Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
377 Q_ASSERT(r && r->global());
379 ScopedString str(scope, argc ? argv[0] : Value::undefinedValue());
381 QString s = str->toQString();
383 int offset = r->lastIndex();
384 if (offset < 0 || offset > s.size()) {
389 Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 *
sizeof(uint));
390 const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets);
393 regExpCtor->d()->clearLastMatch();
400 ReturnedValue retVal = Encode::undefined();
402 if (r->value()->captureCount()) {
403 int start = matchOffsets[0];
404 int end = matchOffsets[1];
405 retVal = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
408 RegExpCtor::Data *dd = regExpCtor->d();
409 dd->lastInput.set(scope.engine, str->d());
410 dd->lastMatchStart = matchOffsets[0];
411 dd->lastMatchEnd = matchOffsets[1];
413 r->setLastIndex(matchOffsets[1]);
540ReturnedValue
RegExpPrototype::method_match(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
543 ScopedObject rx(scope, thisObject);
545 return scope.engine->throwTypeError();
546 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
547 if (scope.hasException())
548 return Encode::undefined();
549 bool global = ScopedValue(scope, rx->get(scope.engine->id_global()))->toBoolean();
552 return exec(scope.engine, rx, s);
554 bool unicode = ScopedValue(scope, rx->get(scope.engine->id_unicode()))->toBoolean();
556 rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0));
557 ScopedArrayObject a(scope, scope.engine->newArrayObject());
560 ScopedValue result(scope);
561 ScopedValue match(scope);
562 ScopedString matchString(scope);
563 ScopedValue v(scope);
565 result = exec(scope.engine, rx, s);
566 if (scope.hasException())
567 return Encode::undefined();
568 if (result->isNull()) {
570 return Encode::null();
571 return a->asReturnedValue();
573 Q_ASSERT(result->isObject());
574 match =
static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
575 matchString = match->toString(scope.engine);
576 if (scope.hasException())
577 return Encode::undefined();
578 a->push_back(matchString);
579 advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
598ReturnedValue
RegExpPrototype::method_replace(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
601 ScopedObject rx(scope, thisObject);
603 return scope.engine->throwTypeError();
605 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
606 if (scope.hasException())
607 return Encode::undefined();
609 int lengthS = s->toQString().size();
611 ScopedString replaceValue(scope);
612 ScopedFunctionObject replaceFunction(scope, (argc > 1 ? argv[1] : Value::undefinedValue()));
613 bool functionalReplace = !!replaceFunction;
614 if (!functionalReplace)
615 replaceValue = (argc > 1 ? argv[1] : Value::undefinedValue()).toString(scope.engine);
617 ScopedValue v(scope);
618 bool global = (v = rx->get(scope.engine->id_global()))->toBoolean();
619 bool unicode =
false;
621 unicode = (v = rx->get(scope.engine->id_unicode()))->toBoolean();
622 if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
623 return scope.engine->throwTypeError();
626 ScopedArrayObject results(scope, scope.engine->newArrayObject());
627 ScopedValue result(scope);
628 ScopedValue match(scope);
629 ScopedString matchString(scope);
631 result = exec(scope.engine, rx, s);
632 if (scope.hasException())
633 return Encode::undefined();
634 if (result->isNull())
636 results->push_back(result);
639 match =
static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
640 matchString = match->toString(scope.engine);
641 if (scope.hasException())
642 return Encode::undefined();
643 advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
645 QString accumulatedResult;
646 int nextSourcePosition = 0;
647 int resultsLength = results->getLength();
648 ScopedObject resultObject(scope);
649 for (
int i = 0; i < resultsLength; ++i) {
650 resultObject = results->get(PropertyKey::fromArrayIndex(i));
651 if (scope.hasException())
652 return Encode::undefined();
654 int nCaptures = resultObject->getLength();
655 nCaptures = qMax(nCaptures - 1, 0);
656 match = resultObject->get(PropertyKey::fromArrayIndex(0));
657 matchString = match->toString(scope.engine);
658 if (scope.hasException())
659 return Encode::undefined();
660 QString m = matchString->toQString();
661 int matchLength = m.size();
662 v = resultObject->get(scope.engine->id_index());
663 int position = v->toInt32();
664 position = qMax(qMin(position, lengthS), 0);
665 if (scope.hasException())
666 return Encode::undefined();
669 Scope innerScope(scope.engine);
670 JSCallArguments cData(scope, nCaptures + 3);
671 while (n <= nCaptures) {
672 v = resultObject->get(PropertyKey::fromArrayIndex(n));
673 if (!v->isUndefined())
674 cData.args[n] = v->toString(scope.engine);
678 if (functionalReplace) {
679 cData.args[0] = matchString;
680 cData.args[nCaptures + 1] = Encode(position);
681 cData.args[nCaptures + 2] = s;
682 ScopedValue replValue(scope, replaceFunction->call(cData));
683 if (scope.hasException())
684 return Encode::undefined();
685 replacement = replValue->toQString();
687 replacement = RegExp::getSubstitution(matchString->toQString(), s->toQString(), position, cData.args, nCaptures, replaceValue->toQString());
689 if (scope.hasException())
690 return Encode::undefined();
691 if (position >= nextSourcePosition) {
692 accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition, position - nextSourcePosition) + replacement;
693 nextSourcePosition = position + matchLength;
696 if (nextSourcePosition < lengthS) {
697 accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition);
699 return scope.engine->newString(accumulatedResult)->asReturnedValue();
702ReturnedValue
RegExpPrototype::method_search(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
705 ScopedObject rx(scope, thisObject);
707 return scope.engine->throwTypeError();
709 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
710 if (scope.hasException())
711 return Encode::undefined();
713 ScopedValue previousLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
714 if (previousLastIndex->toNumber() != 0) {
715 if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
716 return scope.engine->throwTypeError();
719 ScopedValue result(scope, exec(scope.engine, rx, s));
720 if (scope.hasException())
721 return Encode::undefined();
723 ScopedValue currentLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
724 if (!currentLastIndex->sameValue(previousLastIndex)) {
725 if (!rx->put(scope.engine->id_lastIndex(), previousLastIndex))
726 return scope.engine->throwTypeError();
729 if (result->isNull())
731 ScopedObject o(scope, result);
733 return o->get(scope.engine->id_index());
750ReturnedValue
RegExpPrototype::method_split(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
753 ScopedObject rx(scope, thisObject);
755 return scope.engine->throwTypeError();
757 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
758 if (scope.hasException())
759 return Encode::undefined();
761 ScopedValue flagsValue(scope, rx->get(scope.engine->id_flags()));
762 ScopedString flags(scope, flagsValue->toString(scope.engine));
763 if (scope.hasException())
764 return Encode::undefined();
765 QString flagsString = flags->toQString();
766 if (!flagsString.contains(QLatin1Char(
'y')))
767 flags = scope.engine->newString(flagsString + QLatin1Char(
'y'));
768 bool unicodeMatching = flagsString.contains(QLatin1Char(
'u'));
770 const FunctionObject *C = rx->speciesConstructor(scope, scope.engine->regExpCtor());
772 return Encode::undefined();
774 Value *args = scope.constructUndefined(2);
777 ScopedObject splitter(scope, C->callAsConstructor(args, 2, f));
778 if (scope.hasException())
779 return Encode::undefined();
781 ScopedArrayObject A(scope, scope.engine->newArrayObject());
783 uint limit = argc < 2 ? UINT_MAX : argv[1].toUInt32();
785 return A->asReturnedValue();
787 QString S = s->toQString();
790 ScopedValue z(scope, exec(scope.engine, splitter, s));
793 return A->asReturnedValue();
798 ScopedValue v(scope);
799 ScopedValue z(scope);
800 ScopedObject zz(scope);
801 ScopedString t(scope);
803 Value qq = Value::fromInt32(q);
804 if (!splitter->put(scope.engine->id_lastIndex(), qq))
805 return scope.engine->throwTypeError();
806 z = exec(scope.engine, splitter, s);
807 if (scope.hasException())
808 return Encode::undefined();
811 q = advanceStringIndex(q, S, unicodeMatching);
815 v = splitter->get(scope.engine->id_lastIndex());
816 int e = qMin(v->toInt32(), size);
818 q = advanceStringIndex(q, S, unicodeMatching);
821 QString T = S.mid(p, q - p);
822 t = scope.engine->newString(T);
825 if (lengthA == limit)
826 return A->asReturnedValue();
829 uint numberOfCaptures = qMax(zz->getLength() - 1, 0ll);
830 for (uint i = 1; i <= numberOfCaptures; ++i) {
831 v = zz->get(PropertyKey::fromArrayIndex(i));
834 if (lengthA == limit)
835 return A->asReturnedValue();
840 QString T = S.mid(p);
841 t = scope.engine->newString(T);
843 return A->asReturnedValue();