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

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