Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4globalobject.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// Qt-Security score:significant
4
6
7#include <private/qv4alloca_p.h>
8#include <private/qv4codegen_p.h>
9#include <private/qv4context_p.h>
10#include <private/qv4function_p.h>
11#include <private/qv4mm_p.h>
12#include <private/qv4scopedvalue_p.h>
13#include <private/qv4script_p.h>
14#include <private/qv4stackframe_p.h>
15#include <private/qv4string_p.h>
16#include <private/qv4value_p.h>
17
18#include <wtf/MathExtras.h>
19
20#include <QtCore/private/qlocale_tools_p.h>
21#include <QtCore/private/qtools_p.h>
22
23#include <QtCore/qdebug.h>
24#include <QtCore/qstring.h>
25
26#include <iostream>
27
28using namespace QV4;
29using QtMiscUtils::toHexUpper;
30using QtMiscUtils::fromHex;
31
32static QString escape(const QString &input)
33{
34 QString output;
35 output.reserve(input.size() * 3);
36 const int length = input.size();
37 for (int i = 0; i < length; ++i) {
38 ushort uc = input.at(i).unicode();
39 if (uc < 0x100) {
40 if ( (uc > 0x60 && uc < 0x7B)
41 || (uc > 0x3F && uc < 0x5B)
42 || (uc > 0x2C && uc < 0x3A)
43 || (uc == 0x2A)
44 || (uc == 0x2B)
45 || (uc == 0x5F)) {
46 output.append(QChar(uc));
47 } else {
48 output.append(u'%');
49 output.append(QLatin1Char(toHexUpper(uc >> 4)));
50 output.append(QLatin1Char(toHexUpper(uc)));
51 }
52 } else {
53 output.append(u'%');
54 output.append(u'u');
55 output.append(QLatin1Char(toHexUpper(uc >> 12)));
56 output.append(QLatin1Char(toHexUpper(uc >> 8)));
57 output.append(QLatin1Char(toHexUpper(uc >> 4)));
58 output.append(QLatin1Char(toHexUpper(uc)));
59 }
60 }
61 return output;
62}
63
64static QString unescape(const QString &input)
65{
66 QString result;
67 result.reserve(input.size());
68 int i = 0;
69 const int length = input.size();
70 while (i < length) {
71 QChar c = input.at(i++);
72 if ((c == u'%') && (i + 1 < length)) {
73 QChar a = input.at(i);
74 if ((a == u'u') && (i + 4 < length)) {
75 int d3 = fromHex(input.at(i+1).unicode());
76 int d2 = fromHex(input.at(i+2).unicode());
77 int d1 = fromHex(input.at(i+3).unicode());
78 int d0 = fromHex(input.at(i+4).unicode());
79 if ((d3 != -1) && (d2 != -1) && (d1 != -1) && (d0 != -1)) {
80 ushort uc = ushort((d3 << 12) | (d2 << 8) | (d1 << 4) | d0);
81 result.append(QChar(uc));
82 i += 5;
83 } else {
84 result.append(c);
85 }
86 } else {
87 int d1 = fromHex(a.unicode());
88 int d0 = fromHex(input.at(i+1).unicode());
89 if ((d1 != -1) && (d0 != -1)) {
90 c = QChar((d1 << 4) | d0);
91 i += 2;
92 }
93 result.append(c);
94 }
95 } else {
96 result.append(c);
97 }
98 }
99 return result;
100}
101
102static const char uriReserved[] = ";/?:@&=+$,#";
103static const char uriUnescaped[] = "-_.!~*'()";
104static const char uriUnescapedReserved[] = "-_.!~*'();/?:@&=+$,#";
105
106static void addEscapeSequence(QString &output, uchar ch)
107{
108 output.append(QLatin1Char('%'));
109 output.append(QLatin1Char(toHexUpper(ch >> 4)));
110 output.append(QLatin1Char(toHexUpper(ch & 0xf)));
111}
112
113static QString encode(const QString &input, const char *unescapedSet, bool *ok)
114{
115 *ok = true;
116 QString output;
117 const int length = input.size();
118 int i = 0;
119 while (i < length) {
120 const QChar c = input.at(i);
121 bool escape = true;
122 if ((c.unicode() >= 'a' && c.unicode() <= 'z') ||
123 (c.unicode() >= 'A' && c.unicode() <= 'Z') ||
124 (c.unicode() >= '0' && c.unicode() <= '9')) {
125 escape = false;
126 } else {
127 const char *r = unescapedSet;
128 while (*r) {
129 if (*r == c.unicode()) {
130 escape = false;
131 break;
132 }
133 ++r;
134 }
135 }
136 if (escape) {
137 uint uc = c.unicode();
138 if ((uc >= 0xDC00) && (uc <= 0xDFFF)) {
139 *ok = false;
140 break;
141 }
142 if (!((uc < 0xD800) || (uc > 0xDBFF))) {
143 ++i;
144 if (i == length) {
145 *ok = false;
146 break;
147 }
148 const uint uc2 = input.at(i).unicode();
149 if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) {
150 *ok = false;
151 break;
152 }
153 uc = ((uc - 0xD800) * 0x400) + (uc2 - 0xDC00) + 0x10000;
154 }
155 if (uc < 0x80) {
156 addEscapeSequence(output, (uchar)uc);
157 } else {
158 if (uc < 0x0800) {
159 addEscapeSequence(output, 0xc0 | ((uchar) (uc >> 6)));
160 } else {
161
162 if (QChar::requiresSurrogates(uc)) {
163 addEscapeSequence(output, 0xf0 | ((uchar) (uc >> 18)));
164 addEscapeSequence(output, 0x80 | (((uchar) (uc >> 12)) & 0x3f));
165 } else {
166 addEscapeSequence(output, 0xe0 | (((uchar) (uc >> 12)) & 0x3f));
167 }
168 addEscapeSequence(output, 0x80 | (((uchar) (uc >> 6)) & 0x3f));
169 }
170 addEscapeSequence(output, 0x80 | ((uchar) (uc&0x3f)));
171 }
172 } else {
173 output.append(c);
174 }
175 ++i;
176 }
177 if (i != length)
178 *ok = false;
179 return output;
180}
181
186
187static QString decode(const QString &input, DecodeMode decodeMode, bool *ok)
188{
189 *ok = true;
190 QString output;
191 output.reserve(input.size());
192 const int length = input.size();
193 int i = 0;
194 const QChar percent = QLatin1Char('%');
195 while (i < length) {
196 const QChar ch = input.at(i);
197 if (ch == percent) {
198 int start = i;
199 if (i + 2 >= length)
200 goto error;
201
202 int d1 = fromHex(input.at(i+1).unicode());
203 int d0 = fromHex(input.at(i+2).unicode());
204 if ((d1 == -1) || (d0 == -1))
205 goto error;
206
207 int b = (d1 << 4) | d0;
208 i += 2;
209 if (b & 0x80) {
210 int uc;
211 int min_uc;
212 int need;
213 if ((b & 0xe0) == 0xc0) {
214 uc = b & 0x1f;
215 need = 1;
216 min_uc = 0x80;
217 } else if ((b & 0xf0) == 0xe0) {
218 uc = b & 0x0f;
219 need = 2;
220 min_uc = 0x800;
221 } else if ((b & 0xf8) == 0xf0) {
222 uc = b & 0x07;
223 need = 3;
224 min_uc = 0x10000;
225 } else {
226 goto error;
227 }
228
229 if (i + (3 * need) >= length)
230 goto error;
231
232 for (int j = 0; j < need; ++j) {
233 ++i;
234 if (input.at(i) != percent)
235 goto error;
236
237 d1 = fromHex(input.at(i+1).unicode());
238 d0 = fromHex(input.at(i+2).unicode());
239 if ((d1 == -1) || (d0 == -1))
240 goto error;
241
242 b = (d1 << 4) | d0;
243 if ((b & 0xC0) != 0x80)
244 goto error;
245
246 i += 2;
247 uc = (uc << 6) + (b & 0x3f);
248 }
249 if (uc < min_uc)
250 goto error;
251
252 if (uc < 0x10000) {
253 output.append(QChar(uc));
254 } else {
255 if (uc > 0x10FFFF)
256 goto error;
257
258 ushort l = ushort(((uc - 0x10000) & 0x3FF) + 0xDC00);
259 ushort h = ushort((((uc - 0x10000) >> 10) & 0x3FF) + 0xD800);
260 output.append(QChar(h));
261 output.append(QChar(l));
262 }
263 } else {
264 if (decodeMode == DecodeNonReserved && b <= 0x40) {
265 const char *r = uriReserved;
266 while (*r) {
267 if (*r == b)
268 break;
269 ++r;
270 }
271 if (*r)
272 output.append(QStringView{input}.mid(start, i - start + 1));
273 else
274 output.append(QChar(b));
275 } else {
276 output.append(QChar(b));
277 }
278 }
279 } else {
280 output.append(ch);
281 }
282 ++i;
283 }
284 if (i != length)
285 *ok = false;
286 return output;
287 error:
288 *ok = false;
289 return QString();
290}
291
293
294void Heap::EvalFunction::init(QV4::ExecutionEngine *engine)
295{
296 Scope s(engine);
297 Heap::FunctionObject::init(engine, s.engine->id_eval());
298 ScopedFunctionObject f(s, this);
299 f->defineReadonlyConfigurableProperty(s.engine->id_length(), Value::fromInt32(1));
300}
301
302static ExecutionContext *evalContext(QV4::ExecutionEngine *v4, bool directCall)
303{
304 // In case of !directCall, the context for eval should be the global scope
305 if (!directCall)
306 return v4->scriptContext();
307
308 // Otherwise there has to be a current stack frame. We need to be called from somewhere.
309 Q_ASSERT(v4->currentStackFrame);
310 return v4->currentContext();
311}
312
313ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const
314{
315 if (argc < 1)
316 return Encode::undefined();
317
318 ExecutionEngine *v4 = engine();
319 const Function *v4Function = v4->currentStackFrame
320 ? v4->currentStackFrame->v4Function
321 : v4->globalCode;
322 const bool isStrict = v4Function && v4Function->isStrict();
323
324 Scope scope(v4);
325
326 ScopedContext ctx(scope, evalContext(v4, directCall));
327
328 String *scode = argv[0].stringValue();
329 if (!scode)
330 return argv[0].asReturnedValue();
331
332 const QString code = scode->toQString();
333 bool inheritContext = !isStrict;
334
335 Script script(ctx, QV4::Compiler::ContextType::Eval, code, QStringLiteral("eval code"));
336 script.setStrictMode(directCall && isStrict);
337 script.setInheritContext(inheritContext);
338 script.parse();
339 if (v4->hasException)
340 return Encode::undefined();
341
342 Function *function = script.function();
343 if (!function)
344 return Encode::undefined();
345 function->kind = Function::Eval;
346
347 ScopedValue thisObject(scope, directCall
348 ? scope.engine->currentStackFrame->thisObject()
349 : scope.engine->globalObject->asReturnedValue());
350 if (function->isStrict() || isStrict) {
351 ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function));
352 return checkedResult(v4, e->call(thisObject, nullptr, 0));
353 }
354
355 return checkedResult(v4, function->call(thisObject, nullptr, 0, ctx));
356}
357
358
359ReturnedValue EvalFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
360{
361 // indirect call
362 return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false);
363}
364
365
366static inline int toInt(const QChar &qc, int R)
367{
368 ushort c = qc.unicode();
369 int v = -1;
370 if (c >= '0' && c <= '9')
371 v = c - '0';
372 else if (c >= 'A' && c <= 'Z')
373 v = c - 'A' + 10;
374 else if (c >= 'a' && c <= 'z')
375 v = c - 'a' + 10;
376 if (v >= 0 && v < R)
377 return v;
378 else
379 return -1;
380}
381
382// parseInt [15.1.2.2]
383ReturnedValue GlobalFunctions::method_parseInt(const FunctionObject *b, const Value *, const Value *argv, int argc)
384{
385 Scope scope(b);
386 ScopedValue inputString(scope, argc ? argv[0] : Value::undefinedValue());
387 ScopedValue radix(scope, argc > 1 ? argv[1] : Value::undefinedValue());
388 int R = radix->isUndefined() ? 0 : radix->toInt32();
389
390 // [15.1.2.2] step by step:
391 QString trimmed = inputString->toQString().trimmed(); // 1 + 2
393
394 const QChar *pos = trimmed.constData();
395 const QChar *end = pos + trimmed.size();
396
397 int sign = 1; // 3
398 if (pos != end) {
399 if (*pos == QLatin1Char('-'))
400 sign = -1; // 4
401 if (*pos == QLatin1Char('-') || *pos == QLatin1Char('+'))
402 ++pos; // 5
403 }
404 bool stripPrefix = true; // 7
405 if (R) { // 8
406 if (R < 2 || R > 36)
407 RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN())); // 8a
408 if (R != 16)
409 stripPrefix = false; // 8b
410 } else { // 9
411 R = 10; // 9a
412 }
413 if (stripPrefix) { // 10
414 if ((end - pos >= 2)
415 && (pos[0] == QLatin1Char('0'))
416 && (pos[1] == QLatin1Char('x') || pos[1] == QLatin1Char('X'))) { // 10a
417 pos += 2;
418 R = 16;
419 }
420 }
421 // 11: Z is progressively built below
422 // 13: this is handled by the toInt function
423 if (pos == end) // 12
424 RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN()));
425 bool overflow = false;
426 qint64 v_overflow = 0;
427 unsigned overflow_digit_count = 0;
428 int d = toInt(*pos++, R);
429 if (d == -1)
430 RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN()));
431 qint64 v = d;
432 while (pos != end) {
433 d = toInt(*pos++, R);
434 if (d == -1)
435 break;
436 if (overflow) {
437 if (overflow_digit_count == 0) {
438 v_overflow = v;
439 v = 0;
440 }
441 ++overflow_digit_count;
442 v = v * R + d;
443 } else {
444 qint64 vNew = v * R + d;
445 if (vNew < v) {
446 overflow = true;
447 --pos;
448 } else {
449 v = vNew;
450 }
451 }
452 }
453
454 if (overflow) {
455 double result = (double) v_overflow * pow(static_cast<double>(R), static_cast<double>(overflow_digit_count));
456 result += v;
457 RETURN_RESULT(Encode(sign * result));
458 } else {
459 RETURN_RESULT(Encode(sign * (double) v)); // 15
460 }
461}
462
463// parseFloat [15.1.2.3]
464ReturnedValue GlobalFunctions::method_parseFloat(const FunctionObject *b, const Value *, const Value *argv, int argc)
465{
466 Scope scope(b);
467 // [15.1.2.3] step by step:
468 ScopedString inputString(scope, argc ? argv[0] : Value::undefinedValue(), ScopedString::Convert);
470
471 QString trimmed = inputString->toQString().trimmed(); // 2
472
473 // 4:
474 if (trimmed.startsWith(QLatin1String("Infinity"))
475 || trimmed.startsWith(QLatin1String("+Infinity")))
476 RETURN_RESULT(Encode(Q_INFINITY));
477 if (trimmed.startsWith(QLatin1String("-Infinity")))
478 RETURN_RESULT(Encode(-Q_INFINITY));
479 QByteArray ba = trimmed.toLatin1();
480 bool ok;
481 const char *begin = ba.constData();
482 const char *end = nullptr;
483 double d = qstrtod(begin, &end, &ok);
484 if (end - begin == 0)
485 RETURN_RESULT(Encode(std::numeric_limits<double>::quiet_NaN())); // 3
486 else
487 RETURN_RESULT(Encode(d));
488}
489
490/// isNaN [15.1.2.4]
491ReturnedValue GlobalFunctions::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc)
492{
493 if (!argc)
494 // undefined gets converted to NaN
495 RETURN_RESULT(Encode(true));
496
497 if (argv[0].integerCompatible())
498 RETURN_RESULT(Encode(false));
499
500 double d = argv[0].toNumber();
501 RETURN_RESULT(Encode((bool)std::isnan(d)));
502}
503
504/// isFinite [15.1.2.5]
505ReturnedValue GlobalFunctions::method_isFinite(const FunctionObject *, const Value *, const Value *argv, int argc)
506{
507 if (!argc)
508 // undefined gets converted to NaN
509 RETURN_RESULT(Encode(false));
510
511 if (argv[0].integerCompatible())
512 RETURN_RESULT(Encode(true));
513
514 double d = argv[0].toNumber();
515 RETURN_RESULT(Encode((bool)std::isfinite(d)));
516}
517
518/// decodeURI [15.1.3.1]
519ReturnedValue GlobalFunctions::method_decodeURI(const FunctionObject *b, const Value *, const Value *argv, int argc)
520{
521 if (argc == 0)
523
524 ExecutionEngine *v4 = b->engine();
525 QString uriString = argv[0].toQString();
526 bool ok;
527 QString out = decode(uriString, DecodeNonReserved, &ok);
528 if (!ok) {
529 Scope scope(v4);
530 ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
531 RETURN_RESULT(scope.engine->throwURIError(s));
532 }
533
534 RETURN_RESULT(v4->newString(out));
535}
536
537/// decodeURIComponent [15.1.3.2]
538ReturnedValue GlobalFunctions::method_decodeURIComponent(const FunctionObject *b, const Value *, const Value *argv, int argc)
539{
540 if (argc == 0)
542
543 ExecutionEngine *v4 = b->engine();
544 QString uriString = argv[0].toQString();
545 bool ok;
546 QString out = decode(uriString, DecodeAll, &ok);
547 if (!ok) {
548 Scope scope(v4);
549 ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
550 RETURN_RESULT(scope.engine->throwURIError(s));
551 }
552
553 RETURN_RESULT(v4->newString(out));
554}
555
556/// encodeURI [15.1.3.3]
557ReturnedValue GlobalFunctions::method_encodeURI(const FunctionObject *b, const Value *, const Value *argv, int argc)
558{
559 if (argc == 0)
561
562 ExecutionEngine *v4 = b->engine();
563 QString uriString = argv[0].toQString();
564 bool ok;
565 QString out = encode(uriString, uriUnescapedReserved, &ok);
566 if (!ok) {
567 Scope scope(v4);
568 ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
569 RETURN_RESULT(scope.engine->throwURIError(s));
570 }
571
572 RETURN_RESULT(v4->newString(out));
573}
574
575/// encodeURIComponent [15.1.3.4]
576ReturnedValue GlobalFunctions::method_encodeURIComponent(const FunctionObject *b, const Value *, const Value *argv, int argc)
577{
578 if (argc == 0)
580
581 ExecutionEngine *v4 = b->engine();
582 QString uriString = argv[0].toQString();
583 bool ok;
584 QString out = encode(uriString, uriUnescaped, &ok);
585 if (!ok) {
586 Scope scope(v4);
587 ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence")));
588 RETURN_RESULT(scope.engine->throwURIError(s));
589 }
590
591 RETURN_RESULT(v4->newString(out));
592}
593
594ReturnedValue GlobalFunctions::method_escape(const FunctionObject *b, const Value *, const Value *argv, int argc)
595{
596 ExecutionEngine *v4 = b->engine();
597 if (!argc)
598 RETURN_RESULT(v4->newString(QStringLiteral("undefined")));
599
600 QString str = argv[0].toQString();
601 RETURN_RESULT(v4->newString(escape(str)));
602}
603
604ReturnedValue GlobalFunctions::method_unescape(const FunctionObject *b, const Value *, const Value *argv, int argc)
605{
606 ExecutionEngine *v4 = b->engine();
607 if (!argc)
608 RETURN_RESULT(v4->newString(QStringLiteral("undefined")));
609
610 QString str = argv[0].toQString();
611 RETURN_RESULT(v4->newString(unescape(str)));
612}
Definition qjsvalue.h:23
Scoped< String > ScopedString
static ExecutionContext * evalContext(QV4::ExecutionEngine *v4, bool directCall)
static void addEscapeSequence(QString &output, uchar ch)
static QString encode(const QString &input, const char *unescapedSet, bool *ok)
static QString unescape(const QString &input)
static int toInt(const QChar &qc, int R)
static const char uriUnescapedReserved[]
DEFINE_OBJECT_VTABLE(EvalFunction)
static const char uriReserved[]
static QString escape(const QString &input)
static QString decode(const QString &input, DecodeMode decodeMode, bool *ok)
static const char uriUnescaped[]
@ DecodeNonReserved
@ DecodeAll
#define CHECK_EXCEPTION()
#define RETURN_UNDEFINED()
#define RETURN_RESULT(r)
Scope(ExecutionEngine *e)