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
qv4script.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
5#include "qv4script_p.h"
6
7#include <private/qqmlengine_p.h>
8#include <private/qqmljsast_p.h>
9#include <private/qqmljsengine_p.h>
10#include <private/qqmljslexer_p.h>
11#include <private/qqmljsparser_p.h>
12#include <private/qqmlsourcecoordinate_p.h>
13#include <private/qv4context_p.h>
14#include <private/qv4function_p.h>
15#include <private/qv4mm_p.h>
16#include <private/qv4profiling_p.h>
17#include <private/qv4runtimecodegen_p.h>
18#include <private/qv4scopedvalue_p.h>
19
20#include <QtCore/qdebug.h>
21#include <QtCore/qscopedvaluerollback.h>
22#include <QtCore/qstring.h>
23
25
26using namespace QV4;
27using namespace QQmlJS;
28
29Script::Script(
30 ExecutionEngine *v4, QmlContext *qml,
31 const QQmlRefPointer<ExecutableCompilationUnit> &compilationUnit)
32 : m_compilationUnit(compilationUnit)
33 , m_context(v4->rootContext())
34 , m_parseAsBinding(true)
35 , m_inheritContext(true)
36 , m_parsed(true)
37{
38 if (qml)
39 m_qmlContext.set(v4, *qml);
40
41 m_vmFunction.set(v4, compilationUnit ? compilationUnit->rootFunction() : nullptr);
42}
43
44Script::~Script()
45{
46}
47
48void Script::parse()
49{
50 if (m_parsed)
51 return;
52
53 m_parsed = true;
54
55 ExecutionEngine *v4 = m_context->engine();
56 Scope valueScope(v4);
57
58 QV4::Compiler::Module module(m_sourceFile, m_sourceFile, v4->debugger() != nullptr);
59
60 if (m_sourceCode.startsWith(QLatin1String("function("))) {
61 static const int snippetLength = 70;
62 v4->throwSyntaxError(
63 QLatin1String(
64 "Using function expressions as statements in scripts is not compliant "
65 "with the ECMAScript specification:"
66 "\n%1...\n"
67 "If you want a function expression, surround it by parentheses.")
68 .arg(QStringView{m_sourceCode}.left(snippetLength)));
69 return;
70 }
71
72 Engine ee, *engine = &ee;
73 Lexer lexer(engine);
74 lexer.setCode(m_sourceCode, m_line, m_parseAsBinding);
75 Parser parser(engine);
76
77 const bool parsed = parser.parseProgram();
78
79 const auto diagnosticMessages = parser.diagnosticMessages();
80 for (const DiagnosticMessage &m : diagnosticMessages) {
81 if (m.isError()) {
82 valueScope.engine->throwSyntaxError(
83 m.message, m_sourceFile, m.loc.startLine, m.loc.startColumn);
84 return;
85 } else {
86 qWarning() << m_sourceFile << ':' << m.loc.startLine << ':' << m.loc.startColumn
87 << ": warning: " << m.message;
88 }
89 }
90
91 if (parsed) {
92 using namespace AST;
93 Program *program = AST::cast<Program *>(parser.rootNode());
94 if (!program) {
95 // if parsing was successful, and we have no program, then
96 // we're done...:
97 return;
98 }
99
100 QV4::Compiler::JSUnitGenerator jsGenerator(&module);
101 RuntimeCodegen cg(v4, &jsGenerator, m_strictMode);
102 if (m_inheritContext)
103 cg.setUseFastLookups(false);
104 cg.generateFromProgram(m_sourceCode, program, &module, m_contextType);
105 if (v4->hasException)
106 return;
107
108 m_compilationUnit = v4->insertCompilationUnit(cg.generateCompilationUnit());
109 m_vmFunction.set(v4, m_compilationUnit->rootFunction());
110 }
111
112 if (!m_vmFunction) {
113 // ### FIX file/line number
114 ScopedObject error(valueScope, v4->newSyntaxErrorObject(QStringLiteral("Syntax error")));
115 v4->throwError(error);
116 }
117}
118
119ReturnedValue Script::run(const QV4::Value *thisObject)
120{
121 if (!m_parsed)
122 parse();
123 if (!m_vmFunction)
124 return Encode::undefined();
125
126 QV4::ExecutionEngine *engine = m_context->engine();
127 QV4::Scope valueScope(engine);
128
129 if (m_qmlContext.isUndefined()) {
130 QScopedValueRollback<Function*> savedGlobalCode(engine->globalCode, m_vmFunction);
131
132 return m_vmFunction->call(thisObject ? thisObject : engine->globalObject, nullptr, 0,
133 m_context);
134 } else {
135 Scoped<QmlContext> qml(valueScope, m_qmlContext.value());
136 return m_vmFunction->call(thisObject, nullptr, 0, qml);
137 }
138}
139
140Function *Script::function()
141{
142 if (!m_parsed)
143 parse();
144 return m_vmFunction;
145}
146
147QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(
148 QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine,
149 Compiler::JSUnitGenerator *unitGenerator, const QString &fileName,
150 const QString &source, QList<QQmlError> *reportedErrors,
151 QV4::Compiler::ContextType contextType, InheritContext inheritContext)
152{
153 using namespace QV4::Compiler;
154 using namespace QQmlJS::AST;
155
156 Lexer lexer(jsEngine);
157 lexer.setCode(source, /*line*/1, /*qml mode*/false);
158 Parser parser(jsEngine);
159
160 parser.parseProgram();
161
162 QList<QQmlError> errors
163 = QQmlEnginePrivate::qmlErrorFromDiagnostics(fileName, parser.diagnosticMessages());
164 if (!errors.isEmpty()) {
165 if (reportedErrors)
166 *reportedErrors << errors;
167 return nullptr;
168 }
169
170 Program *program = AST::cast<Program *>(parser.rootNode());
171 if (!program) {
172 // if parsing was successful, and we have no program, then
173 // we're done...:
174 return nullptr;
175 }
176
177 Codegen cg(unitGenerator, /*strict mode*/false);
178 if (inheritContext == InheritContext::Yes)
179 cg.setUseFastLookups(false);
180 cg.generateFromProgram(source, program, module, contextType);
181 if (cg.hasError()) {
182 if (reportedErrors) {
183 const auto v4Error = cg.error();
184 QQmlError error;
185 error.setUrl(cg.url());
186 error.setLine(qmlConvertSourceCoordinate<quint32, int>(v4Error.loc.startLine));
187 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(v4Error.loc.startColumn));
188 error.setDescription(v4Error.message);
189 reportedErrors->append(error);
190 }
191 return nullptr;
192 }
193
194 return cg.generateCompilationUnit(/*generate unit data*/false);
195}
196
197QT_END_NAMESPACE