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 | |
16 | using namespace QV4; |
17 | |
18 | DEFINE_OBJECT_VTABLE(Module); |
19 | |
20 | void 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 | |
68 | void 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 | |
93 | const 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 | |
109 | ReturnedValue 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 | |
128 | PropertyAttributes 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 | |
150 | bool 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 | |
160 | bool Module::virtualPreventExtensions(Managed *) |
161 | { |
162 | return true; |
163 | } |
164 | |
165 | bool Module::virtualDefineOwnProperty(Managed *, PropertyKey, const Property *, PropertyAttributes) |
166 | { |
167 | return false; |
168 | } |
169 | |
170 | bool Module::virtualPut(Managed *, PropertyKey, const Value &, Value *) |
171 | { |
172 | return false; |
173 | } |
174 | |
175 | bool 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 | |
186 | struct 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 | |
196 | PropertyKey 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 | |
217 | OwnPropertyKeyIterator *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 | |
234 | Heap::Object *Module::virtualGetPrototypeOf(const Managed *) |
235 | { |
236 | return nullptr; |
237 | } |
238 | |
239 | bool Module::virtualSetPrototypeOf(Managed *, const Object *proto) |
240 | { |
241 | return proto == nullptr; |
242 | } |
243 | |
244 | bool Module::virtualIsExtensible(const Managed *) |
245 | { |
246 | return false; |
247 | } |
248 | |