1/****************************************************************************
2**
3** Copyright (C) 2018 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
41#include "qv4module_p.h"
42
43#include <private/qv4mm_p.h>
44#include <private/qv4vme_moth_p.h>
45#include <private/qv4context_p.h>
46#include <private/qv4symbol_p.h>
47#include <private/qv4identifiertable_p.h>
48
49#include <QScopeGuard>
50
51using namespace QV4;
52
53DEFINE_OBJECT_VTABLE(Module);
54
55void Heap::Module::init(ExecutionEngine *engine, ExecutableCompilationUnit *moduleUnit)
56{
57 Object::init();
58
59 // This is a back pointer and there is no need to call addref() on the unit, because the unit
60 // owns this object instead.
61 unit = moduleUnit;
62 self.set(e: engine, b: this);
63
64 Function *moduleFunction = unit->runtimeFunctions[unit->unitData()->indexOfRootFunction];
65
66 const uint locals = moduleFunction->compiledFunction->nLocals;
67 const size_t requiredMemory = sizeof(QV4::CallContext::Data) - sizeof(Value) + sizeof(Value) * locals;
68 scope.set(e: engine, newVal: engine->memoryManager->allocManaged<QV4::CallContext>(size: requiredMemory, ic: moduleFunction->internalClass));
69 scope->init();
70 scope->outer.set(e: engine, newVal: engine->rootContext()->d());
71 scope->locals.size = locals;
72 scope->locals.alloc = locals;
73 scope->nArgs = 0;
74
75 // Prepare the temporal dead zone
76 scope->setupLocalTemporalDeadZone(moduleFunction->compiledFunction);
77
78 Scope valueScope(engine);
79
80 // It's possible for example to re-export an import, for example:
81 // import * as foo from "./bar.js"
82 // export { foo }
83 // Since we don't add imports to the locals, it won't be found typically.
84 // Except now we add imports at the end of the internal class in the index
85 // space past the locals, so that resolveExport can find it.
86 {
87 Scoped<QV4::InternalClass> ic(valueScope, scope->internalClass);
88
89 for (uint i = 0; i < unit->data->importEntryTableSize; ++i) {
90 const CompiledData::ImportEntry &import = unit->data->importEntryTable()[i];
91 ic = ic->addMember(identifier: engine->identifierTable->asPropertyKey(str: unit->runtimeStrings[import.localName]), data: Attr_NotConfigurable);
92 }
93 scope->internalClass.set(e: engine, newVal: ic->d());
94 }
95
96
97 Scoped<QV4::Module> This(valueScope, this);
98 ScopedString name(valueScope, engine->newString(QStringLiteral("Module")));
99 This->insertMember(s: engine->symbol_toStringTag(), v: name, attributes: Attr_ReadOnly);
100 This->setPrototypeUnchecked(nullptr);
101}
102
103void Module::evaluate()
104{
105 if (d()->evaluated)
106 return;
107 d()->evaluated = true;
108
109 ExecutableCompilationUnit *unit = d()->unit;
110
111 unit->evaluateModuleRequests();
112
113 ExecutionEngine *v4 = engine();
114 Function *moduleFunction = unit->runtimeFunctions[unit->data->indexOfRootFunction];
115 CppStackFrame frame;
116 frame.init(engine: v4, v4Function: moduleFunction, argv: nullptr, argc: 0);
117 frame.setupJSFrame(stackSpace: v4->jsStackTop, function: Value::undefinedValue(), scope: d()->scope,
118 thisObject: Value::undefinedValue(), newTarget: Value::undefinedValue());
119
120 frame.push();
121 v4->jsStackTop += frame.requiredJSStackFrameSize();
122 auto frameCleanup = qScopeGuard(f: [&frame]() {
123 frame.pop();
124 });
125 Moth::VME::exec(frame: &frame, engine: v4);
126}
127
128const Value *Module::resolveExport(PropertyKey id) const
129{
130 if (d()->unit->isESModule()) {
131 if (!id.isString())
132 return nullptr;
133 Scope scope(engine());
134 ScopedString name(scope, id.asStringOrSymbol());
135 return d()->unit->resolveExport(exportName: name);
136 } else {
137 InternalClassEntry entry = d()->scope->internalClass->find(id);
138 if (entry.isValid())
139 return &d()->scope->locals[entry.index];
140 return nullptr;
141 }
142}
143
144ReturnedValue Module::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
145{
146 if (id.isSymbol())
147 return Object::virtualGet(m, id, receiver, hasProperty);
148
149 const Module *module = static_cast<const Module *>(m);
150 const Value *v = module->resolveExport(id);
151 if (hasProperty)
152 *hasProperty = v != nullptr;
153 if (!v)
154 return Encode::undefined();
155 if (v->isEmpty()) {
156 Scope scope(m->engine());
157 ScopedValue propName(scope, id.toStringOrSymbol(e: scope.engine));
158 return scope.engine->throwReferenceError(value: propName);
159 }
160 return v->asReturnedValue();
161}
162
163PropertyAttributes Module::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
164{
165 if (id.isSymbol())
166 return Object::virtualGetOwnProperty(m, id, p);
167
168 const Module *module = static_cast<const Module *>(m);
169 const Value *v = module->resolveExport(id);
170 if (!v) {
171 if (p)
172 p->value = Encode::undefined();
173 return Attr_Invalid;
174 }
175 if (p)
176 p->value = v->isEmpty() ? Encode::undefined() : v->asReturnedValue();
177 if (v->isEmpty()) {
178 Scope scope(m->engine());
179 ScopedValue propName(scope, id.toStringOrSymbol(e: scope.engine));
180 scope.engine->throwReferenceError(value: propName);
181 }
182 return Attr_Data | Attr_NotConfigurable;
183}
184
185bool Module::virtualHasProperty(const Managed *m, PropertyKey id)
186{
187 if (id.isSymbol())
188 return Object::virtualHasProperty(m, id);
189
190 const Module *module = static_cast<const Module *>(m);
191 const Value *v = module->resolveExport(id);
192 return v != nullptr;
193}
194
195bool Module::virtualPreventExtensions(Managed *)
196{
197 return true;
198}
199
200bool Module::virtualDefineOwnProperty(Managed *, PropertyKey, const Property *, PropertyAttributes)
201{
202 return false;
203}
204
205bool Module::virtualPut(Managed *, PropertyKey, const Value &, Value *)
206{
207 return false;
208}
209
210bool Module::virtualDeleteProperty(Managed *m, PropertyKey id)
211{
212 if (id.isSymbol())
213 return Object::virtualDeleteProperty(m, id);
214 const Module *module = static_cast<const Module *>(m);
215 const Value *v = module->resolveExport(id);
216 if (v)
217 return false;
218 return true;
219}
220
221struct ModuleNamespaceIterator : ObjectOwnPropertyKeyIterator
222{
223 QStringList exportedNames;
224 int exportIndex = 0;
225 ModuleNamespaceIterator(const QStringList &names) : exportedNames(names) {}
226 ~ModuleNamespaceIterator() override = default;
227 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
228
229};
230
231PropertyKey ModuleNamespaceIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
232{
233 const Module *module = static_cast<const Module *>(o);
234 if (exportIndex < exportedNames.count()) {
235 if (attrs)
236 *attrs = Attr_Data;
237 Scope scope(module->engine());
238 ScopedString exportName(scope, scope.engine->newString(s: exportedNames.at(i: exportIndex)));
239 exportIndex++;
240 const Value *v = module->resolveExport(id: exportName->toPropertyKey());
241 if (pd) {
242 if (v->isEmpty())
243 scope.engine->throwReferenceError(value: exportName);
244 else
245 pd->value = *v;
246 }
247 return exportName->toPropertyKey();
248 }
249 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
250}
251
252OwnPropertyKeyIterator *Module::virtualOwnPropertyKeys(const Object *o, Value *target)
253{
254 const Module *module = static_cast<const Module *>(o);
255 *target = *o;
256
257 QStringList names;
258 if (module->d()->unit->isESModule()) {
259 names = module->d()->unit->exportedNames();
260 } else {
261 Heap::InternalClass *scopeClass = module->d()->scope->internalClass;
262 for (uint i = 0; i < scopeClass->size; ++i)
263 names << scopeClass->keyAt(index: i);
264 }
265
266 return new ModuleNamespaceIterator(names);
267}
268
269Heap::Object *Module::virtualGetPrototypeOf(const Managed *)
270{
271 return nullptr;
272}
273
274bool Module::virtualSetPrototypeOf(Managed *, const Object *proto)
275{
276 return proto == nullptr;
277}
278
279bool Module::virtualIsExtensible(const Managed *)
280{
281 return false;
282}
283

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