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

source code of qtdeclarative/src/qml/jsruntime/qv4globalobject.cpp