Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qv4regexpobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4regexpobject_p.h"
5#include "qv4regexp_p.h"
6#include <private/qv4mm_p.h>
7#include "qv4scopedvalue_p.h"
8#include "qv4jscall_p.h"
9#include "qv4symbol_p.h"
10
11#include "private/qlocale_tools_p.h"
12
13#include <QtCore/QDebug>
14#if QT_CONFIG(regularexpression)
15#include <QtCore/qregularexpression.h>
16#endif
17#include <cassert>
18#include <typeinfo>
19#include <iostream>
20#include <private/qv4alloca_p.h>
21
23
24using namespace QV4;
25
27
28void Heap::RegExpObject::init()
29{
30 Object::init();
31 Scope scope(internalClass->engine);
32 Scoped<QV4::RegExpObject> o(scope, this);
33 value.set(scope.engine, QV4::RegExp::create(scope.engine, QString(), CompiledData::RegExp::RegExp_NoFlags));
34 o->initProperties();
35}
36
37void Heap::RegExpObject::init(QV4::RegExp *value)
38{
39 Object::init();
40 Scope scope(internalClass->engine);
41 this->value.set(scope.engine, value->d());
42 Scoped<QV4::RegExpObject> o(scope, this);
43 o->initProperties();
44}
45
47{
48 QString ecmaPattern;
49 int len = pattern.size();
50 ecmaPattern.reserve(len);
51 int i = 0;
52 const QChar *wc = pattern.unicode();
53 bool inBracket = false;
54 while (i < len) {
55 QChar c = wc[i++];
56 ecmaPattern += c;
57 switch (c.unicode()) {
58 case '?':
59 case '+':
60 case '*':
61 case '}':
62 if (!inBracket)
63 ecmaPattern += QLatin1Char('?');
64 break;
65 case '\\':
66 if (i < len)
67 ecmaPattern += wc[i++];
68 break;
69 case '[':
70 inBracket = true;
71 break;
72 case ']':
73 inBracket = false;
74 break;
75 default:
76 break;
77 }
78 }
79 return ecmaPattern;
80}
81
82#if QT_CONFIG(regularexpression)
83// Converts a QRegularExpression to a JS RegExp.
84// The conversion is not 100% exact since ECMA regexp and QRegularExpression
85// have different semantics/flags, but we try to do our best.
86void Heap::RegExpObject::init(const QRegularExpression &re)
87{
88 Object::init();
89
90 Scope scope(internalClass->engine);
91 Scoped<QV4::RegExpObject> o(scope, this);
92
93 QRegularExpression::PatternOptions options = re.patternOptions();
96 : CompiledData::RegExp::RegExp_NoFlags;
99 QString pattern = re.pattern();
102 o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, pattern, flags));
103 o->initProperties();
104}
105#endif
106
113
114#if QT_CONFIG(regularexpression)
115// Converts a JS RegExp to a QRegularExpression.
116// The conversion is not 100% exact since ECMA regexp and QRegularExpression
117// have different semantics/flags, but we try to do our best.
118QRegularExpression RegExpObject::toQRegularExpression() const
119{
120 QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption;
125 return QRegularExpression(*value()->pattern, options);
126}
127#endif
128
130{
131 QString p = *value()->pattern;
132 if (p.isEmpty()) {
133 p = QStringLiteral("(?:)");
134 } else {
135 // escape certain parts, see ch. 15.10.4
136 p.replace(u'/', QLatin1String("\\/"));
137 }
138 return p;
139}
140
142{
143 QString s = str->toQString();
144
145 Scope scope(engine);
146 int offset = (global() || sticky()) ? lastIndex() : 0;
147 if (offset < 0 || offset > s.size()) {
148 setLastIndex(0);
150 }
151
152 Q_ALLOCA_VAR(uint, matchOffsets, value()->captureCount() * 2 * sizeof(uint));
153 const uint result = Scoped<RegExp>(scope, value())->match(s, offset, matchOffsets);
154
155 RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor());
156 regExpCtor->d()->clearLastMatch();
157
158 if (result == JSC::Yarr::offsetNoMatch) {
159 if (global() || sticky())
160 setLastIndex(0);
162 }
163
164 Q_ASSERT(result <= uint(std::numeric_limits<int>::max()));
165
166 // fill in result data
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];
175 if (end > strlen)
176 end = strlen;
178 array->arrayPut(i, v);
179 }
180 array->setArrayLengthUnchecked(len);
181 array->setProperty(Index_ArrayIndex, Value::fromInt32(int(result)));
182 array->setProperty(Index_ArrayInput, *str);
183
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];
189
190 if (global() || sticky())
191 setLastIndex(matchOffsets[1]);
192
193 return array.asReturnedValue();
194}
195
197
198void Heap::RegExpCtor::init(QV4::ExecutionEngine *engine)
199{
200 Heap::FunctionObject::init(engine, QStringLiteral("RegExp"));
201 clearLastMatch();
202}
203
204void Heap::RegExpCtor::clearLastMatch()
205{
206 lastMatch.set(internalClass->engine, Value::nullValue());
207 lastInput.set(internalClass->engine, internalClass->engine->id_empty()->d());
208 lastMatchStart = 0;
209 lastMatchEnd = 0;
210}
211
212static bool isRegExp(ExecutionEngine *e, const QV4::Value *arg)
213{
214 const QV4::Object *o = arg->objectValue();
215 if (!o)
216 return false;
217
219 if (!isRegExp.isUndefined())
220 return isRegExp.toBoolean();
221 const RegExpObject *re = o->as<RegExpObject>();
222 return re ? true : false;
223}
224
226{
228 if (!f->isUndefined()) {
229 ScopedString s(scope, f->toString(scope.engine));
230 if (scope.hasException())
231 return flags;
232 QString str = s->toQString();
233 for (int i = 0; i < str.size(); ++i) {
236 } else if (str.at(i) == QLatin1Char('i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) {
238 } else if (str.at(i) == QLatin1Char('m') && !(flags & CompiledData::RegExp::RegExp_Multiline)) {
240 } else if (str.at(i) == QLatin1Char('u') && !(flags & CompiledData::RegExp::RegExp_Unicode)) {
242 } else if (str.at(i) == QLatin1Char('y') && !(flags & CompiledData::RegExp::RegExp_Sticky)) {
244 } else {
245 scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
246 return flags;
247 }
248 }
249 }
250 return flags;
251}
252
253ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget)
254{
255 Scope scope(fo);
256
257 bool patternIsRegExp = argc ? ::isRegExp(scope.engine, argv) : false;
258
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();
265 }
266 }
267
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);
273
274 if (re) {
275 if (f->isUndefined()) {
276 Scoped<RegExp> regexp(scope, re->value());
277 return Encode(scope.engine->newRegExpObject(regexp));
278 }
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);
291 } else {
292 if (!p->isUndefined())
293 pattern = p->toQString();
294 if (scope.hasException())
295 return Encode::undefined();
296 flags = parseFlags(scope, f);
297 }
298 if (scope.hasException())
299 return Encode::undefined();
300
301 Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, flags));
302 if (!regexp->isValid()) {
303 return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression"));
304 }
305
306 ReturnedValue o = Encode(scope.engine->newRegExpObject(regexp));
307
308 if (!newTarget)
309 return o;
310 ScopedObject obj(scope, o);
311 obj->setProtoFromNewTarget(newTarget);
312 return obj->asReturnedValue();
313}
314
315ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
316{
317 return virtualCallAsConstructor(f, argv, argc, f);
318}
319
321{
322 Scope scope(engine);
323 ScopedObject o(scope);
324 ScopedObject ctor(scope, constructor);
325
326 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
327 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(2));
328 ctor->addSymbolSpecies();
329
330 // Properties deprecated in the spec but required by "the web" :(
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);
350
351 defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
366
367 // another web extension
369}
370
371/* used by String.match */
372ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
373{
374 Scope scope(b);
375 Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
376 Q_ASSERT(r && r->global());
377
378 ScopedString str(scope, argc ? argv[0] : Value::undefinedValue());
379 Q_ASSERT(str);
380 QString s = str->toQString();
381
382 int offset = r->lastIndex();
383 if (offset < 0 || offset > s.size()) {
384 r->setLastIndex(0);
386 }
387
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);
390
391 RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor());
392 regExpCtor->d()->clearLastMatch();
393
394 if (result == -1) {
395 r->setLastIndex(0);
397 }
398
400 // return first match
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();
405 }
406
407 RegExpCtor::Data *dd = regExpCtor->d();
408 dd->lastInput.set(scope.engine, str->d());
409 dd->lastMatchStart = matchOffsets[0];
410 dd->lastMatchEnd = matchOffsets[1];
411
412 r->setLastIndex(matchOffsets[1]);
413
414 return retVal;
415}
416
418{
419 Scope scope(engine);
420 ScopedString key(scope, scope.engine->newString(QStringLiteral("exec")));
421 ScopedFunctionObject exec(scope, o->get(key));
422 if (exec) {
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();
429 }
430 Scoped<RegExpObject> re(scope, o);
431 if (!re)
432 return scope.engine->throwTypeError();
433 return re->builtinExec(engine, s);
434}
435
436ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
437{
438 Scope scope(b);
439 Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
440 if (!r)
441 return scope.engine->throwTypeError();
442
443 ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue());
444 ScopedString str(scope, arg->toString(scope.engine));
445 if (scope.hasException())
447
448 return r->builtinExec(scope.engine, str);
449}
450
452{
453 Scope scope(f);
454 ScopedObject o(scope, thisObject);
455 if (!o)
456 return scope.engine->throwTypeError();
457
459 ScopedValue v(scope);
460 v = o->get(scope.engine->id_global());
461 if (scope.hasException())
462 return Encode::undefined();
463 if (v->toBoolean())
464 result += QLatin1Char('g');
465 v = o->get(scope.engine->id_ignoreCase());
466 if (scope.hasException())
467 return Encode::undefined();
468 if (v->toBoolean())
469 result += QLatin1Char('i');
470 v = o->get(scope.engine->id_multiline());
471 if (scope.hasException())
472 return Encode::undefined();
473 if (v->toBoolean())
474 result += QLatin1Char('m');
475 v = o->get(scope.engine->id_unicode());
476 if (scope.hasException())
477 return Encode::undefined();
478 if (v->toBoolean())
479 result += QLatin1Char('u');
480 v = o->get(scope.engine->id_sticky());
481 if (scope.hasException())
482 return Encode::undefined();
483 if (v->toBoolean())
484 result += QLatin1Char('y');
485 return scope.engine->newString(result)->asReturnedValue();
486}
487
489{
490 Scope scope(f);
491 Scoped<RegExpObject> re(scope, thisObject);
492 if (!re) {
493 if (thisObject->sameValue(*scope.engine->regExpPrototype()))
494 return Encode::undefined();
495 return scope.engine->throwTypeError();
496 }
497
498 bool b = re->value()->flags & CompiledData::RegExp::RegExp_Global;
499 return Encode(b);
500}
501
503{
504 Scope scope(f);
505 Scoped<RegExpObject> re(scope, thisObject);
506 if (!re) {
507 if (thisObject->sameValue(*scope.engine->regExpPrototype()))
508 return Encode::undefined();
509 return scope.engine->throwTypeError();
510 }
511
512 bool b = re->value()->flags & CompiledData::RegExp::RegExp_IgnoreCase;
513 return Encode(b);
514}
515
516static int advanceStringIndex(int index, const QString &str, bool unicode)
517{
518 if (unicode) {
519 if (index < str.size() - 1 &&
520 str.at(index).isHighSurrogate() &&
521 str.at(index + 1).isLowSurrogate())
522 ++index;
523 }
524 ++index;
525 return index;
526}
527
528static void advanceLastIndexOnEmptyMatch(ExecutionEngine *e, bool unicode, QV4::Object *rx, const String *matchString, const QString &str)
529{
530 Scope scope(e);
531 if (matchString->d()->length() == 0) {
532 QV4::ScopedValue v(scope, rx->get(scope.engine->id_lastIndex()));
533 int lastIndex = advanceStringIndex(v->toLength(), str, unicode);
534 if (!rx->put(scope.engine->id_lastIndex(), QV4::Value::fromInt32(lastIndex)))
535 scope.engine->throwTypeError();
536 }
537}
538
539ReturnedValue RegExpPrototype::method_match(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
540{
541 Scope scope(f);
542 ScopedObject rx(scope, thisObject);
543 if (!rx)
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();
549
550 if (!global)
551 return exec(scope.engine, rx, s);
552
553 bool unicode = ScopedValue(scope, rx->get(scope.engine->id_unicode()))->toBoolean();
554
555 rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0));
556 ScopedArrayObject a(scope, scope.engine->newArrayObject());
557 uint n = 0;
558
559 ScopedValue result(scope);
560 ScopedValue match(scope);
561 ScopedString matchString(scope);
562 ScopedValue v(scope);
563 while (1) {
564 result = exec(scope.engine, rx, s);
565 if (scope.hasException())
566 return Encode::undefined();
567 if (result->isNull()) {
568 if (!n)
569 return Encode::null();
570 return a->asReturnedValue();
571 }
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());
579 ++n;
580 }
581}
582
584{
585 Scope scope(f);
586 Scoped<RegExpObject> re(scope, thisObject);
587 if (!re) {
588 if (thisObject->sameValue(*scope.engine->regExpPrototype()))
589 return Encode::undefined();
590 return scope.engine->throwTypeError();
591 }
592
593 bool b = re->value()->flags & CompiledData::RegExp::RegExp_Multiline;
594 return Encode(b);
595}
596
597ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
598{
599 Scope scope(f);
600 ScopedObject rx(scope, thisObject);
601 if (!rx)
602 return scope.engine->throwTypeError();
603
604 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
605 if (scope.hasException())
606 return Encode::undefined();
607
608 int lengthS = s->toQString().size();
609
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);
615
616 ScopedValue v(scope);
617 bool global = (v = rx->get(scope.engine->id_global()))->toBoolean();
618 bool unicode = false;
619 if (global) {
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();
623 }
624
626 ScopedValue result(scope);
627 ScopedValue match(scope);
628 ScopedString matchString(scope);
629 while (1) {
630 result = exec(scope.engine, rx, s);
631 if (scope.hasException())
632 return Encode::undefined();
633 if (result->isNull())
634 break;
636 if (!global)
637 break;
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());
643 }
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();
652
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();
666
667 int n = 1;
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);
674 ++n;
675 }
676 QString replacement;
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();
685 } else {
686 replacement = RegExp::getSubstitution(matchString->toQString(), s->toQString(), position, cData.args, nCaptures, replaceValue->toQString());
687 }
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;
693 }
694 }
695 if (nextSourcePosition < lengthS) {
696 accumulatedResult += QStringView{s->toQString()}.mid(nextSourcePosition);
697 }
698 return scope.engine->newString(accumulatedResult)->asReturnedValue();
699}
700
701ReturnedValue RegExpPrototype::method_search(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
702{
703 Scope scope(f);
704 ScopedObject rx(scope, thisObject);
705 if (!rx)
706 return scope.engine->throwTypeError();
707
708 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
709 if (scope.hasException())
710 return Encode::undefined();
711
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();
716 }
717
718 ScopedValue result(scope, exec(scope.engine, rx, s));
719 if (scope.hasException())
720 return Encode::undefined();
721
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();
726 }
727
728 if (result->isNull())
729 return Encode(-1);
730 ScopedObject o(scope, result);
731 Q_ASSERT(o);
732 return o->get(scope.engine->id_index());
733}
734
735
737{
738 Scope scope(f);
739 Scoped<RegExpObject> re(scope, thisObject);
740 if (!re) {
741 if (thisObject->sameValue(*scope.engine->regExpPrototype()))
742 return scope.engine->newString(QStringLiteral("(?:)"))->asReturnedValue();
743 return scope.engine->throwTypeError();
744 }
745
746 return scope.engine->newString(re->toString())->asReturnedValue();
747}
748
749ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
750{
751 Scope scope(f);
752 ScopedObject rx(scope, thisObject);
753 if (!rx)
754 return scope.engine->throwTypeError();
755
756 ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
757 if (scope.hasException())
758 return Encode::undefined();
759
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'));
768
769 const FunctionObject *C = rx->speciesConstructor(scope, scope.engine->regExpCtor());
770 if (!C)
771 return Encode::undefined();
772
773 Value *args = scope.alloc(2);
774 args[0] = rx;
775 args[1] = flags;
776 ScopedObject splitter(scope, C->callAsConstructor(args, 2, f));
777 if (scope.hasException())
778 return Encode::undefined();
779
780 ScopedArrayObject A(scope, scope.engine->newArrayObject());
781 uint lengthA = 0;
782 uint limit = argc < 2 ? UINT_MAX : argv[1].toUInt32();
783 if (limit == 0)
784 return A->asReturnedValue();
785
786 QString S = s->toQString();
787 int size = S.size();
788 if (size == 0) {
789 ScopedValue z(scope, exec(scope.engine, splitter, s));
790 if (z->isNull())
791 A->push_back(s);
792 return A->asReturnedValue();
793 }
794
795 int p = 0;
796 int q = 0;
797 ScopedValue v(scope);
798 ScopedValue z(scope);
799 ScopedObject zz(scope);
800 ScopedString t(scope);
801 while (q < size) {
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();
808
809 if (z->isNull()) {
810 q = advanceStringIndex(q, S, unicodeMatching);
811 continue;
812 }
813
814 v = splitter->get(scope.engine->id_lastIndex());
815 int e = qMin(v->toInt32(), size);
816 if (e == p) {
817 q = advanceStringIndex(q, S, unicodeMatching);
818 continue;
819 }
820 QString T = S.mid(p, q - p);
821 t = scope.engine->newString(T);
822 A->push_back(t);
823 ++lengthA;
824 if (lengthA == limit)
825 return A->asReturnedValue();
826 p = e;
827 zz = *z;
828 uint numberOfCaptures = qMax(zz->getLength() - 1, 0ll);
829 for (uint i = 1; i <= numberOfCaptures; ++i) {
830 v = zz->get(PropertyKey::fromArrayIndex(i));
831 A->push_back(v);
832 ++lengthA;
833 if (lengthA == limit)
834 return A->asReturnedValue();
835 }
836 q = p;
837 }
838
839 QString T = S.mid(p);
840 t = scope.engine->newString(T);
841 A->push_back(t);
842 return A->asReturnedValue();
843}
844
846{
847 Scope scope(f);
848 Scoped<RegExpObject> re(scope, thisObject);
849 if (!re) {
850 if (thisObject->sameValue(*scope.engine->regExpPrototype()))
851 return Encode::undefined();
852 return scope.engine->throwTypeError();
853 }
854
855 bool b = re->value()->flags & CompiledData::RegExp::RegExp_Sticky;
856 return Encode(b);
857}
858
859ReturnedValue RegExpPrototype::method_test(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
860{
861 Value res = Value::fromReturnedValue(method_exec(b, thisObject, argv, argc));
862 return Encode(!res.isNull());
863}
864
866{
867 Scope scope(b);
868 const Object *r = thisObject->as<Object>();
869 if (!r)
870 return scope.engine->throwTypeError();
871
872 ScopedValue v(scope);
873 v = r->get(scope.engine->id_source());
874 ScopedString source(scope, v->toString(scope.engine));
875 if (scope.hasException())
876 return Encode::undefined();
877 v = r->get(scope.engine->id_flags());
878 ScopedString flags(scope, v->toString(scope.engine));
879 if (scope.hasException())
880 return Encode::undefined();
881
882 QString result = QLatin1Char('/') + source->toQString() + QLatin1Char('/') + flags->toQString();
883 return Encode(scope.engine->newString(result));
884}
885
887{
888 Scope scope(f);
889 Scoped<RegExpObject> re(scope, thisObject);
890 if (!re) {
891 if (thisObject->sameValue(*scope.engine->regExpPrototype()))
892 return Encode::undefined();
893 return scope.engine->throwTypeError();
894 }
895
896 bool b = re->value()->flags & CompiledData::RegExp::RegExp_Unicode;
897 return Encode(b);
898}
899
900ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
901{
902 Scope scope(b);
903 Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
904 if (!r)
905 return scope.engine->throwTypeError();
906
907 Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(argv, argc));
908 if (re) // Otherwise the regexp constructor should have thrown an exception
909 r->d()->value.set(scope.engine, re->value());
910 return Encode::undefined();
911}
912
913template <uint index>
915{
916 Scope scope(b);
917 ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
918 ScopedValue res(scope, lastMatch ? lastMatch->get(index) : Encode::undefined());
919 if (res->isUndefined())
920 res = scope.engine->newString();
921 return res->asReturnedValue();
922}
923
925{
926 Scope scope(b);
927 ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
928 ScopedValue res(scope, lastMatch ? lastMatch->get(lastMatch->getLength() - 1) : Encode::undefined());
929 if (res->isUndefined())
930 res = scope.engine->newString();
931 return res->asReturnedValue();
932}
933
935{
936 return static_cast<RegExpCtor*>(b->engine()->regExpCtor())->lastInput()->asReturnedValue();
937}
938
940{
941 Scope scope(b);
942 Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor());
943 QString lastInput = regExpCtor->lastInput()->toQString();
944 return Encode(scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart())));
945}
946
948{
949 Scope scope(b);
950 Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor());
951 QString lastInput = regExpCtor->lastInput()->toQString();
952 return Encode(scope.engine->newString(lastInput.mid(regExpCtor->lastMatchEnd())));
953}
954
\inmodule QtCore
void push_back(parameter_type t)
Definition qlist.h:675
\inmodule QtCore \reentrant
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
ManagedType::Data * alloc(Args &&... args)
Definition qv4mm_p.h:307
QString str
[2]
Combined button and popup list for selecting options.
Scoped< FunctionObject > ScopedFunctionObject
quint64 ReturnedValue
Scoped< String > ScopedString
static QString toString(QV4::ReturnedValue v)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
n varying highp vec2 A
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
const GLfloat * m
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLfloat GLfloat f
GLbitfield flags
GLuint start
GLenum GLuint GLintptr offset
GLfloat n
GLsizei GLsizei GLchar * source
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
const GLubyte * c
GLenum array
GLint limit
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLsizei len
GLubyte * pattern
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
unsigned int uint
Definition qtypes.h:34
#define Q_ALLOCA_VAR(type, name, size)
Definition qv4alloca_p.h:36
static int advanceStringIndex(int index, const QString &str, bool unicode)
static bool isRegExp(ExecutionEngine *e, const QV4::Value *arg)
static QString minimalPattern(const QString &pattern)
uint parseFlags(Scope &scope, const QV4::Value *f)
static void advanceLastIndexOnEmptyMatch(ExecutionEngine *e, bool unicode, QV4::Object *rx, const String *matchString, const QString &str)
#define RETURN_UNDEFINED()
#define RETURN_RESULT(r)
#define DEFINE_OBJECT_VTABLE(classname)
p rx()++
dialog exec()
args<< 1<< 2;QJSValue threeAgain=fun.call(args);QString fileName="helloworld.qs";QFile scriptFile(fileName);if(!scriptFile.open(QIODevice::ReadOnly)) QTextStream stream(&scriptFile);QString contents=stream.readAll();scriptFile.close();myEngine.evaluate(contents, fileName);myEngine.globalObject().setProperty("myNumber", 123);...QJSValue myNumberPlusOne=myEngine.evaluate("myNumber + 1");QJSValue result=myEngine.evaluate(...);if(result.isError()) qDebug()<< "Uncaught exception at line"<< result.property("lineNumber").toInt()<< ":"<< result.toString();QPushButton *button=new QPushButton;QJSValue scriptButton=myEngine.newQObject(button);myEngine.globalObject().setProperty("button", scriptButton);myEngine.evaluate("button.checkable = true");qDebug()<< scriptButton.property("checkable").toBool();scriptButton.property("show").call();QJSEngine engine;QObject *myQObject=new QObject();myQObject- setProperty)("dynamicProperty", 3)
QJSValueList args
QJSValue global
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18
static constexpr ReturnedValue undefined()
static constexpr ReturnedValue null()
Heap::InternalClass * internalClasses(InternalClassType icType)
MemoryManager * memoryManager
String * id_sticky() const
Symbol * symbol_split() const
String * id_unicode() const
String * id_flags() const
FunctionObject * regExpCtor() const
String * id_length() const
String * id_constructor() const
String * id_global() const
String * id_multiline() const
String * id_ignoreCase() const
Symbol * symbol_replace() const
String * id_toString() const
String * id_prototype() const
String * id_lastIndex() const
Heap::String * newString(char16_t c)
String * id_index() const
Symbol * symbol_match() const
Heap::ArrayObject * newArrayObject(int count=0)
ReturnedValue throwSyntaxError(const QString &message)
String * id_source() const
Object * regExpPrototype() const
Heap::RegExpObject * newRegExpObject(const QString &pattern, int flags)
ReturnedValue throwTypeError()
Symbol * symbol_search() const
ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget=nullptr) const
ReturnedValue asReturnedValue() const
Definition qv4value_p.h:342
ExecutionEngine * engine() const
void defineDefaultProperty(StringOrSymbol *name, const Value &value, PropertyAttributes attributes=Attr_Data|Attr_NotEnumerable)
void defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter)
bool put(StringOrSymbol *name, const Value &v, Value *receiver=nullptr)
ReturnedValue get(StringOrSymbol *name, bool *hasProperty=nullptr, const Value *receiver=nullptr) const
static PropertyKey fromArrayIndex(uint idx)
Heap::String * lastInput()
ReturnedValue builtinExec(ExecutionEngine *engine, const String *s)
QString toString() const
RegExp * value() const
void setLastIndex(int index)
static ReturnedValue method_get_leftContext(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue execFirstMatch(const FunctionObject *b, 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_get_unicode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_flags(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_get_lastMatch_n(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_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_multiline(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
void init(ExecutionEngine *engine, Object *ctor)
static ReturnedValue method_exec(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_input(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_rightContext(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_replace(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 exec(ExecutionEngine *engine, const Object *o, const String *s)
static ReturnedValue method_get_ignoreCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_global(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
static QString getSubstitution(const QString &matched, const QString &str, int position, const Value *captures, int nCaptures, const QString &replacement)
static Heap::RegExp * create(ExecutionEngine *engine, const QString &pattern, uint flags=CompiledData::RegExp::RegExp_NoFlags)
int captureCount() const
V4_NEEDS_DESTROY QString pattern() const
Definition qv4regexp_p.h:86
Value * alloc(qint64 nValues) const =delete
bool hasException() const
ExecutionEngine * engine
constexpr ReturnedValue asReturnedValue() const
bool isUndefined() const
static constexpr VTable::CallAsConstructor virtualCallAsConstructor
static constexpr VTable::Call virtualCall
static constexpr Value fromInt32(int i)
Definition qv4value_p.h:187
bool toBoolean() const
Definition qv4value_p.h:97
unsigned int toUInt32() const
Definition qv4value_p.h:364
static constexpr Value undefinedValue()
Definition qv4value_p.h:191
static constexpr Value fromReturnedValue(ReturnedValue val)
Definition qv4value_p.h:165
const T * as() const
Definition qv4value_p.h:132
bool sameValue(Value other) const
Definition qv4value.cpp:226
static constexpr Value nullValue()
Definition qv4value_p.h:195