142ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine,
const String *str)
145 QString s = str->toQString();
148 int offset = (global() || sticky()) ? lastIndex() : 0;
149 if (offset < 0 || offset > s.size()) {
154 Q_ALLOCA_VAR(uint, matchOffsets, value()->captureCount() * 2 *
sizeof(uint));
155 const uint result = Scoped<RegExp>(scope, value())->match(s, offset, matchOffsets);
157 RegExpCtor *regExpCtor =
static_cast<RegExpCtor *>(scope.engine->regExpCtor());
158 regExpCtor->d()->clearLastMatch();
160 if (result == JSC::Yarr::offsetNoMatch) {
161 if (global() || sticky())
166 Q_ASSERT(result <= uint(std::numeric_limits<
int>::max()));
169 ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray)));
170 int len = value()->captureCount();
171 array->arrayReserve(len);
172 ScopedValue v(scope);
173 int strlen = s.size();
174 for (
int i = 0; i < len; ++i) {
175 int start = matchOffsets[i * 2];
176 int end = matchOffsets[i * 2 + 1];
179 v = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
180 array->arrayPut(i, v);
182 array->setArrayLengthUnchecked(len);
183 array->setProperty(Index_ArrayIndex, Value::fromInt32(
int(result)));
184 array->setProperty(Index_ArrayInput, *str);
186 RegExpCtor::Data *dd = regExpCtor->d();
187 dd->lastMatch.set(scope.engine, array);
188 dd->lastInput.set(scope.engine, str->d());
189 dd->lastMatchStart = matchOffsets[0];
190 dd->lastMatchEnd = matchOffsets[1];
192 if (global() || sticky())
193 setLastIndex(matchOffsets[1]);
195 return array.asReturnedValue();
229 CompiledData::RegExp::Flags flags = CompiledData::RegExp::RegExp_NoFlags;
230 if (!f->isUndefined()) {
231 ScopedString s(scope, f->toString(scope.engine));
232 if (scope.hasException())
234 QString str = s->toQString();
235 for (
int i = 0; i < str.size(); ++i) {
236 if (str.at(i) == QLatin1Char(
'g') && !(flags & CompiledData::RegExp::RegExp_Global)) {
237 flags |= CompiledData::RegExp::RegExp_Global;
238 }
else if (str.at(i) == QLatin1Char(
'i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) {
239 flags |= CompiledData::RegExp::RegExp_IgnoreCase;
240 }
else if (str.at(i) == QLatin1Char(
'm') && !(flags & CompiledData::RegExp::RegExp_Multiline)) {
241 flags |= CompiledData::RegExp::RegExp_Multiline;
242 }
else if (str.at(i) == QLatin1Char(
'u') && !(flags & CompiledData::RegExp::RegExp_Unicode)) {
243 flags |= CompiledData::RegExp::RegExp_Unicode;
244 }
else if (str.at(i) == QLatin1Char(
'y') && !(flags & CompiledData::RegExp::RegExp_Sticky)) {
245 flags |= CompiledData::RegExp::RegExp_Sticky;
247 scope.engine->throwSyntaxError(QStringLiteral(
"Invalid flags supplied to RegExp constructor"));
255ReturnedValue RegExpCtor::virtualCallAsConstructor(
const FunctionObject *fo,
const Value *argv,
int argc,
const Value *newTarget)
259 bool patternIsRegExp = argc ? ::isRegExp(scope.engine, argv) :
false;
261 if (newTarget == fo) {
262 if (patternIsRegExp && (argc < 2 || argv[1].isUndefined())) {
263 const Object *pattern =
static_cast<
const Object *>(argv);
264 ScopedValue patternConstructor(scope, pattern->get(scope.engine->id_constructor()));
265 if (patternConstructor->sameValue(*newTarget))
266 return pattern->asReturnedValue();
270 ScopedValue p(scope, argc ? argv[0] : Value::undefinedValue());
271 ScopedValue f(scope, argc > 1 ? argv[1] : Value::undefinedValue());
272 Scoped<RegExpObject> re(scope, p);
274 CompiledData::RegExp::Flags flags = CompiledData::RegExp::RegExp_NoFlags;
277 if (f->isUndefined()) {
278 Scoped<RegExp> regexp(scope, re->value());
279 return Encode(scope.engine->newRegExpObject(regexp));
281 pattern = *re->value()->pattern;
282 flags = parseFlags(scope, f);
283 }
else if (patternIsRegExp) {
284 const Object *po =
static_cast<
const Object *>(argv);
285 p = po->get(scope.engine->id_source());
286 if (!p->isUndefined())
287 pattern = p->toQString();
288 if (scope.hasException())
289 return Encode::undefined();
290 if (f->isUndefined())
291 f = po->get(scope.engine->id_flags());
292 flags = parseFlags(scope, f);
294 if (!p->isUndefined())
295 pattern = p->toQString();
296 if (scope.hasException())
297 return Encode::undefined();
298 flags = parseFlags(scope, f);
300 if (scope.hasException())
301 return Encode::undefined();
303 Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, flags));
304 if (!regexp->isValid()) {
305 return scope.engine->throwSyntaxError(QStringLiteral(
"Invalid regular expression"));
308 ReturnedValue o = Encode(scope.engine->newRegExpObject(regexp));
312 ScopedObject obj(scope, o);
313 obj->setProtoFromNewTarget(newTarget);
314 return obj->asReturnedValue();
322void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
325 ScopedObject o(scope);
326 ScopedObject ctor(scope, constructor);
328 ctor->defineReadonlyProperty(engine->id_prototype(), (o =
this));
329 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(2));
330 ctor->addSymbolSpecies();
333 ctor->defineAccessorProperty(QStringLiteral(
"lastMatch"), method_get_lastMatch_n<0>,
nullptr);
334 ctor->defineAccessorProperty(QStringLiteral(
"$&"), method_get_lastMatch_n<0>,
nullptr);
335 ctor->defineAccessorProperty(QStringLiteral(
"$1"), method_get_lastMatch_n<1>,
nullptr);
336 ctor->defineAccessorProperty(QStringLiteral(
"$2"), method_get_lastMatch_n<2>,
nullptr);
337 ctor->defineAccessorProperty(QStringLiteral(
"$3"), method_get_lastMatch_n<3>,
nullptr);
338 ctor->defineAccessorProperty(QStringLiteral(
"$4"), method_get_lastMatch_n<4>,
nullptr);
339 ctor->defineAccessorProperty(QStringLiteral(
"$5"), method_get_lastMatch_n<5>,
nullptr);
340 ctor->defineAccessorProperty(QStringLiteral(
"$6"), method_get_lastMatch_n<6>,
nullptr);
341 ctor->defineAccessorProperty(QStringLiteral(
"$7"), method_get_lastMatch_n<7>,
nullptr);
342 ctor->defineAccessorProperty(QStringLiteral(
"$8"), method_get_lastMatch_n<8>,
nullptr);
343 ctor->defineAccessorProperty(QStringLiteral(
"$9"), method_get_lastMatch_n<9>,
nullptr);
344 ctor->defineAccessorProperty(QStringLiteral(
"lastParen"), method_get_lastParen,
nullptr);
345 ctor->defineAccessorProperty(QStringLiteral(
"$+"), method_get_lastParen,
nullptr);
346 ctor->defineAccessorProperty(QStringLiteral(
"input"), method_get_input,
nullptr);
347 ctor->defineAccessorProperty(QStringLiteral(
"$_"), method_get_input,
nullptr);
348 ctor->defineAccessorProperty(QStringLiteral(
"leftContext"), method_get_leftContext,
nullptr);
349 ctor->defineAccessorProperty(QStringLiteral(
"$`"), method_get_leftContext,
nullptr);
350 ctor->defineAccessorProperty(QStringLiteral(
"rightContext"), method_get_rightContext,
nullptr);
351 ctor->defineAccessorProperty(QStringLiteral(
"$'"), method_get_rightContext,
nullptr);
353 defineDefaultProperty(QStringLiteral(
"constructor"), (o = ctor));
354 defineAccessorProperty(scope.engine->id_flags(), method_get_flags,
nullptr);
355 defineAccessorProperty(scope.engine->id_global(), method_get_global,
nullptr);
356 defineAccessorProperty(scope.engine->id_ignoreCase(), method_get_ignoreCase,
nullptr);
357 defineDefaultProperty(QStringLiteral(
"exec"), method_exec, 1);
358 defineDefaultProperty(engine->symbol_match(), method_match, 1);
359 defineAccessorProperty(scope.engine->id_multiline(), method_get_multiline,
nullptr);
360 defineDefaultProperty(engine->symbol_replace(), method_replace, 2);
361 defineDefaultProperty(engine->symbol_search(), method_search, 1);
362 defineAccessorProperty(scope.engine->id_source(), method_get_source,
nullptr);
363 defineDefaultProperty(engine->symbol_split(), method_split, 2);
364 defineAccessorProperty(scope.engine->id_sticky(), method_get_sticky,
nullptr);
365 defineDefaultProperty(QStringLiteral(
"test"), method_test, 1);
366 defineDefaultProperty(engine->id_toString(), method_toString, 0);
367 defineAccessorProperty(scope.engine->id_unicode(), method_get_unicode,
nullptr);
370 defineDefaultProperty(QStringLiteral(
"compile"), method_compile, 2);
374ReturnedValue RegExpPrototype::execFirstMatch(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
378 Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
379 Q_ASSERT(r && r->global());
381 ScopedString str(scope, argc ? argv[0] : Value::undefinedValue());
383 QString s = str->toQString();
385 int offset = r->lastIndex();
386 if (offset < 0 || offset > s.size()) {
391 Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 *
sizeof(uint));
392 const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets);
394 RegExpCtor *regExpCtor =
static_cast<RegExpCtor *>(scope.engine->regExpCtor());
395 regExpCtor->d()->clearLastMatch();
402 ReturnedValue retVal = Encode::undefined();
404 if (r->value()->captureCount()) {
405 int start = matchOffsets[0];
406 int end = matchOffsets[1];
407 retVal = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
410 RegExpCtor::Data *dd = regExpCtor->d();
411 dd->lastInput.set(scope.engine, str->d());
412 dd->lastMatchStart = matchOffsets[0];
413 dd->lastMatchEnd = matchOffsets[1];
415 r->setLastIndex(matchOffsets[1]);
420ReturnedValue RegExpPrototype::exec(ExecutionEngine *engine,
const Object *o,
const String *s)
423 ScopedString key(scope, scope.engine->newString(QStringLiteral(
"exec")));
424 ScopedFunctionObject exec(scope, o->get(key));
426 ScopedValue result(scope, exec->call(o, s, 1));
427 if (scope.hasException())
429 if (!result->isNull() && !result->isObject())
430 return scope.engine->throwTypeError();
431 return result->asReturnedValue();
433 Scoped<RegExpObject> re(scope, o);
435 return scope.engine->throwTypeError();
436 return re->builtinExec(engine, s);
542ReturnedValue RegExpPrototype::method_match(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
545 ScopedObject rx(scope, thisObject);
547 return scope.engine->throwTypeError();
548 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
549 if (scope.hasException())
550 return Encode::undefined();
551 bool global = ScopedValue(scope, rx->get(scope.engine->id_global()))->toBoolean();
554 return exec(scope.engine, rx, s);
556 bool unicode = ScopedValue(scope, rx->get(scope.engine->id_unicode()))->toBoolean();
558 rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0));
559 ScopedArrayObject a(scope, scope.engine->newArrayObject());
562 ScopedValue result(scope);
563 ScopedValue match(scope);
564 ScopedString matchString(scope);
565 ScopedValue v(scope);
567 result = exec(scope.engine, rx, s);
568 if (scope.hasException())
569 return Encode::undefined();
570 if (result->isNull()) {
572 return Encode::null();
573 return a->asReturnedValue();
575 Q_ASSERT(result->isObject());
576 match =
static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
577 matchString = match->toString(scope.engine);
578 if (scope.hasException())
579 return Encode::undefined();
580 a->push_back(matchString);
581 advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
600ReturnedValue RegExpPrototype::method_replace(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
603 ScopedObject rx(scope, thisObject);
605 return scope.engine->throwTypeError();
607 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
608 if (scope.hasException())
609 return Encode::undefined();
611 int lengthS = s->toQString().size();
613 ScopedString replaceValue(scope);
614 ScopedFunctionObject replaceFunction(scope, (argc > 1 ? argv[1] : Value::undefinedValue()));
615 bool functionalReplace = !!replaceFunction;
616 if (!functionalReplace)
617 replaceValue = (argc > 1 ? argv[1] : Value::undefinedValue()).toString(scope.engine);
619 ScopedValue v(scope);
620 bool global = (v = rx->get(scope.engine->id_global()))->toBoolean();
621 bool unicode =
false;
623 unicode = (v = rx->get(scope.engine->id_unicode()))->toBoolean();
624 if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
625 return scope.engine->throwTypeError();
628 ScopedArrayObject results(scope, scope.engine->newArrayObject());
629 ScopedValue result(scope);
630 ScopedValue match(scope);
631 ScopedString matchString(scope);
633 result = exec(scope.engine, rx, s);
634 if (scope.hasException())
635 return Encode::undefined();
636 if (result->isNull())
638 results->push_back(result);
641 match =
static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
642 matchString = match->toString(scope.engine);
643 if (scope.hasException())
644 return Encode::undefined();
645 advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
647 QString accumulatedResult;
648 int nextSourcePosition = 0;
649 int resultsLength = results->getLength();
650 ScopedObject resultObject(scope);
651 for (
int i = 0; i < resultsLength; ++i) {
652 resultObject = results->get(PropertyKey::fromArrayIndex(i));
653 if (scope.hasException())
654 return Encode::undefined();
656 int nCaptures = resultObject->getLength();
657 nCaptures = qMax(nCaptures - 1, 0);
658 match = resultObject->get(PropertyKey::fromArrayIndex(0));
659 matchString = match->toString(scope.engine);
660 if (scope.hasException())
661 return Encode::undefined();
662 QString m = matchString->toQString();
663 int matchLength = m.size();
664 v = resultObject->get(scope.engine->id_index());
665 int position = v->toInt32();
666 position = qMax(qMin(position, lengthS), 0);
667 if (scope.hasException())
668 return Encode::undefined();
671 Scope innerScope(scope.engine);
672 JSCallArguments cData(scope, nCaptures + 3);
673 while (n <= nCaptures) {
674 v = resultObject->get(PropertyKey::fromArrayIndex(n));
675 if (!v->isUndefined())
676 cData.args[n] = v->toString(scope.engine);
680 if (functionalReplace) {
681 cData.args[0] = matchString;
682 cData.args[nCaptures + 1] = Encode(position);
683 cData.args[nCaptures + 2] = s;
684 ScopedValue replValue(scope, replaceFunction->call(cData));
685 if (scope.hasException())
686 return Encode::undefined();
687 replacement = replValue->toQString();
689 replacement = RegExp::getSubstitution(matchString->toQString(), s->toQString(), position, cData.args, nCaptures, replaceValue->toQString());
691 if (scope.hasException())
692 return Encode::undefined();
693 if (position >= nextSourcePosition) {
694 accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition, position - nextSourcePosition) + replacement;
695 nextSourcePosition = position + matchLength;
698 if (nextSourcePosition < lengthS) {
699 accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition);
701 return scope.engine->newString(accumulatedResult)->asReturnedValue();
704ReturnedValue RegExpPrototype::method_search(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
707 ScopedObject rx(scope, thisObject);
709 return scope.engine->throwTypeError();
711 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
712 if (scope.hasException())
713 return Encode::undefined();
715 ScopedValue previousLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
716 if (previousLastIndex->toNumber() != 0) {
717 if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
718 return scope.engine->throwTypeError();
721 ScopedValue result(scope, exec(scope.engine, rx, s));
722 if (scope.hasException())
723 return Encode::undefined();
725 ScopedValue currentLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
726 if (!currentLastIndex->sameValue(previousLastIndex)) {
727 if (!rx->put(scope.engine->id_lastIndex(), previousLastIndex))
728 return scope.engine->throwTypeError();
731 if (result->isNull())
733 ScopedObject o(scope, result);
735 return o->get(scope.engine->id_index());
752ReturnedValue RegExpPrototype::method_split(
const FunctionObject *f,
const Value *thisObject,
const Value *argv,
int argc)
755 ScopedObject rx(scope, thisObject);
757 return scope.engine->throwTypeError();
759 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
760 if (scope.hasException())
761 return Encode::undefined();
763 ScopedValue flagsValue(scope, rx->get(scope.engine->id_flags()));
764 ScopedString flags(scope, flagsValue->toString(scope.engine));
765 if (scope.hasException())
766 return Encode::undefined();
767 QString flagsString = flags->toQString();
768 if (!flagsString.contains(QLatin1Char(
'y')))
769 flags = scope.engine->newString(flagsString + QLatin1Char(
'y'));
770 bool unicodeMatching = flagsString.contains(QLatin1Char(
'u'));
772 const FunctionObject *C = rx->speciesConstructor(scope, scope.engine->regExpCtor());
774 return Encode::undefined();
776 Value *args = scope.constructUndefined(2);
779 ScopedObject splitter(scope, C->callAsConstructor(args, 2, f));
780 if (scope.hasException())
781 return Encode::undefined();
783 ScopedArrayObject A(scope, scope.engine->newArrayObject());
785 uint limit = argc < 2 ? UINT_MAX : argv[1].toUInt32();
787 return A->asReturnedValue();
789 QString S = s->toQString();
792 ScopedValue z(scope, exec(scope.engine, splitter, s));
795 return A->asReturnedValue();
800 ScopedValue v(scope);
801 ScopedValue z(scope);
802 ScopedObject zz(scope);
803 ScopedString t(scope);
805 Value qq = Value::fromInt32(q);
806 if (!splitter->put(scope.engine->id_lastIndex(), qq))
807 return scope.engine->throwTypeError();
808 z = exec(scope.engine, splitter, s);
809 if (scope.hasException())
810 return Encode::undefined();
813 q = advanceStringIndex(q, S, unicodeMatching);
817 v = splitter->get(scope.engine->id_lastIndex());
818 int e = qMin(v->toInt32(), size);
820 q = advanceStringIndex(q, S, unicodeMatching);
823 QString T = S.mid(p, q - p);
824 t = scope.engine->newString(T);
827 if (lengthA == limit)
828 return A->asReturnedValue();
831 uint numberOfCaptures = qMax(zz->getLength() - 1, 0ll);
832 for (uint i = 1; i <= numberOfCaptures; ++i) {
833 v = zz->get(PropertyKey::fromArrayIndex(i));
836 if (lengthA == limit)
837 return A->asReturnedValue();
842 QString T = S.mid(p);
843 t = scope.engine->newString(T);
845 return A->asReturnedValue();