1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef builtin_ModuleObject_h
8 #define builtin_ModuleObject_h
10 #include "mozilla/HashTable.h" // mozilla::{HashMap, DefaultHasher}
11 #include "mozilla/Maybe.h" // mozilla::Maybe
12 #include "mozilla/Span.h"
14 #include <stddef.h> // size_t
15 #include <stdint.h> // int32_t, uint32_t
17 #include "gc/Barrier.h" // HeapPtr
18 #include "gc/ZoneAllocator.h" // CellAllocPolicy
19 #include "js/Class.h" // JSClass, ObjectOpResult
20 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
21 #include "js/GCVector.h"
22 #include "js/Id.h" // jsid
23 #include "js/Modules.h"
24 #include "js/Proxy.h" // BaseProxyHandler
25 #include "js/RootingAPI.h" // Rooted, Handle, MutableHandle
26 #include "js/TypeDecls.h" // HandleValue, HandleId, HandleObject, HandleScript, MutableHandleValue, MutableHandleIdVector, MutableHandleObject
27 #include "js/UniquePtr.h" // UniquePtr
28 #include "vm/JSObject.h" // JSObject
29 #include "vm/NativeObject.h" // NativeObject
30 #include "vm/ProxyObject.h" // ProxyObject
31 #include "vm/SharedStencil.h" // FunctionDeclarationVector
38 class PropertyDescriptor
;
45 class CyclicModuleFields
;
46 class SyntheticModuleFields
;
48 class ModuleEnvironmentObject
;
51 class ScriptSourceObject
;
53 class ModuleRequestObject
: public NativeObject
{
55 enum { SpecifierSlot
= 0, AttributesSlot
, SlotCount
};
57 static const JSClass class_
;
58 static bool isInstance(HandleValue value
);
59 [[nodiscard
]] static ModuleRequestObject
* create(
60 JSContext
* cx
, Handle
<JSAtom
*> specifier
,
61 Handle
<ArrayObject
*> maybeAttributes
);
63 JSAtom
* specifier() const;
64 ArrayObject
* attributes() const;
65 bool hasAttributes() const;
66 static bool getModuleType(JSContext
* cx
,
67 const Handle
<ModuleRequestObject
*> moduleRequest
,
68 JS::ModuleType
& moduleType
);
71 using ModuleRequestVector
=
72 GCVector
<HeapPtr
<ModuleRequestObject
*>, 0, SystemAllocPolicy
>;
75 const HeapPtr
<ModuleRequestObject
*> moduleRequest_
;
76 const HeapPtr
<JSAtom
*> importName_
;
77 const HeapPtr
<JSAtom
*> localName_
;
79 // Line number (1-origin).
80 const uint32_t lineNumber_
;
82 // Column number in UTF-16 code units.
83 const JS::ColumnNumberOneOrigin columnNumber_
;
86 ImportEntry(Handle
<ModuleRequestObject
*> moduleRequest
,
87 Handle
<JSAtom
*> maybeImportName
, Handle
<JSAtom
*> localName
,
88 uint32_t lineNumber
, JS::ColumnNumberOneOrigin columnNumber
);
90 ModuleRequestObject
* moduleRequest() const { return moduleRequest_
; }
91 JSAtom
* importName() const { return importName_
; }
92 JSAtom
* localName() const { return localName_
; }
93 uint32_t lineNumber() const { return lineNumber_
; }
94 JS::ColumnNumberOneOrigin
columnNumber() const { return columnNumber_
; }
96 void trace(JSTracer
* trc
);
99 using ImportEntryVector
= GCVector
<ImportEntry
, 0, SystemAllocPolicy
>;
102 const HeapPtr
<JSAtom
*> exportName_
;
103 const HeapPtr
<ModuleRequestObject
*> moduleRequest_
;
104 const HeapPtr
<JSAtom
*> importName_
;
105 const HeapPtr
<JSAtom
*> localName_
;
107 // Line number (1-origin).
108 const uint32_t lineNumber_
;
110 // Column number in UTF-16 code units.
111 const JS::ColumnNumberOneOrigin columnNumber_
;
114 ExportEntry(Handle
<JSAtom
*> maybeExportName
,
115 Handle
<ModuleRequestObject
*> maybeModuleRequest
,
116 Handle
<JSAtom
*> maybeImportName
, Handle
<JSAtom
*> maybeLocalName
,
117 uint32_t lineNumber
, JS::ColumnNumberOneOrigin columnNumber
);
118 JSAtom
* exportName() const { return exportName_
; }
119 ModuleRequestObject
* moduleRequest() const { return moduleRequest_
; }
120 JSAtom
* importName() const { return importName_
; }
121 JSAtom
* localName() const { return localName_
; }
122 uint32_t lineNumber() const { return lineNumber_
; }
123 JS::ColumnNumberOneOrigin
columnNumber() const { return columnNumber_
; }
125 void trace(JSTracer
* trc
);
128 using ExportEntryVector
= GCVector
<ExportEntry
, 0, SystemAllocPolicy
>;
130 class RequestedModule
{
131 const HeapPtr
<ModuleRequestObject
*> moduleRequest_
;
133 // Line number (1-origin).
134 const uint32_t lineNumber_
;
136 // Column number in UTF-16 code units.
137 const JS::ColumnNumberOneOrigin columnNumber_
;
140 RequestedModule(Handle
<ModuleRequestObject
*> moduleRequest
,
141 uint32_t lineNumber
, JS::ColumnNumberOneOrigin columnNumber
);
142 ModuleRequestObject
* moduleRequest() const { return moduleRequest_
; }
143 uint32_t lineNumber() const { return lineNumber_
; }
144 JS::ColumnNumberOneOrigin
columnNumber() const { return columnNumber_
; }
146 void trace(JSTracer
* trc
);
149 using RequestedModuleVector
= GCVector
<RequestedModule
, 0, SystemAllocPolicy
>;
151 class ResolvedBindingObject
: public NativeObject
{
153 enum { ModuleSlot
= 0, BindingNameSlot
, SlotCount
};
155 static const JSClass class_
;
156 static bool isInstance(HandleValue value
);
157 static ResolvedBindingObject
* create(JSContext
* cx
,
158 Handle
<ModuleObject
*> module
,
159 Handle
<JSAtom
*> bindingName
);
160 ModuleObject
* module() const;
161 JSAtom
* bindingName() const;
164 class IndirectBindingMap
{
166 void trace(JSTracer
* trc
);
168 bool put(JSContext
* cx
, HandleId name
,
169 Handle
<ModuleEnvironmentObject
*> environment
, HandleId targetName
);
171 size_t count() const { return map_
? map_
->count() : 0; }
173 bool has(jsid name
) const { return map_
? map_
->has(name
) : false; }
175 bool lookup(jsid name
, ModuleEnvironmentObject
** envOut
,
176 mozilla::Maybe
<PropertyInfo
>* propOut
) const;
178 template <typename Func
>
179 void forEachExportedName(Func func
) const {
184 for (auto r
= map_
->all(); !r
.empty(); r
.popFront()) {
185 func(r
.front().key());
191 Binding(ModuleEnvironmentObject
* environment
, jsid targetName
,
193 HeapPtr
<ModuleEnvironmentObject
*> environment
;
195 HeapPtr
<jsid
> targetName
;
200 using Map
= mozilla::HashMap
<PreBarriered
<jsid
>, Binding
,
201 mozilla::DefaultHasher
<PreBarriered
<jsid
>>,
204 mozilla::Maybe
<Map
> map_
;
207 // Vector of atoms representing the names exported from a module namespace.
209 // This is used both on the stack and in the heap.
210 using ExportNameVector
= GCVector
<HeapPtr
<JSAtom
*>, 0, SystemAllocPolicy
>;
212 class ModuleNamespaceObject
: public ProxyObject
{
214 enum ModuleNamespaceSlot
{ ExportsSlot
= 0, BindingsSlot
};
216 static bool isInstance(HandleValue value
);
217 static ModuleNamespaceObject
* create(
218 JSContext
* cx
, Handle
<ModuleObject
*> module
,
219 MutableHandle
<UniquePtr
<ExportNameVector
>> exports
,
220 MutableHandle
<UniquePtr
<IndirectBindingMap
>> bindings
);
222 ModuleObject
& module();
223 const ExportNameVector
& exports() const;
224 IndirectBindingMap
& bindings();
226 bool addBinding(JSContext
* cx
, Handle
<JSAtom
*> exportedName
,
227 Handle
<ModuleObject
*> targetModule
,
228 Handle
<JSAtom
*> targetName
);
231 struct ProxyHandler
: public BaseProxyHandler
{
234 bool getOwnPropertyDescriptor(
235 JSContext
* cx
, HandleObject proxy
, HandleId id
,
236 MutableHandle
<mozilla::Maybe
<PropertyDescriptor
>> desc
) const override
;
237 bool defineProperty(JSContext
* cx
, HandleObject proxy
, HandleId id
,
238 Handle
<PropertyDescriptor
> desc
,
239 ObjectOpResult
& result
) const override
;
240 bool ownPropertyKeys(JSContext
* cx
, HandleObject proxy
,
241 MutableHandleIdVector props
) const override
;
242 bool delete_(JSContext
* cx
, HandleObject proxy
, HandleId id
,
243 ObjectOpResult
& result
) const override
;
244 bool getPrototype(JSContext
* cx
, HandleObject proxy
,
245 MutableHandleObject protop
) const override
;
246 bool setPrototype(JSContext
* cx
, HandleObject proxy
, HandleObject proto
,
247 ObjectOpResult
& result
) const override
;
248 bool getPrototypeIfOrdinary(JSContext
* cx
, HandleObject proxy
,
250 MutableHandleObject protop
) const override
;
251 bool setImmutablePrototype(JSContext
* cx
, HandleObject proxy
,
252 bool* succeeded
) const override
;
254 bool preventExtensions(JSContext
* cx
, HandleObject proxy
,
255 ObjectOpResult
& result
) const override
;
256 bool isExtensible(JSContext
* cx
, HandleObject proxy
,
257 bool* extensible
) const override
;
258 bool has(JSContext
* cx
, HandleObject proxy
, HandleId id
,
259 bool* bp
) const override
;
260 bool get(JSContext
* cx
, HandleObject proxy
, HandleValue receiver
,
261 HandleId id
, MutableHandleValue vp
) const override
;
262 bool set(JSContext
* cx
, HandleObject proxy
, HandleId id
, HandleValue v
,
263 HandleValue receiver
, ObjectOpResult
& result
) const override
;
265 void trace(JSTracer
* trc
, JSObject
* proxy
) const override
;
266 void finalize(JS::GCContext
* gcx
, JSObject
* proxy
) const override
;
268 static const char family
;
271 bool hasBindings() const;
272 bool hasExports() const;
274 ExportNameVector
& mutableExports();
277 static const ProxyHandler proxyHandler
;
280 // Value types of [[Status]] in a Cyclic Module Record
281 // https://tc39.es/ecma262/#table-cyclic-module-fields
282 enum class ModuleStatus
: int8_t {
290 // Sub-state of Evaluated with error value set.
292 // This is not returned from ModuleObject::status(); use hadEvaluationError()
297 // Special values for CyclicModuleFields' asyncEvaluatingPostOrderSlot field,
298 // which is used as part of the implementation of the AsyncEvaluation field of
299 // cyclic module records.
301 // The spec requires us to be able to tell the order in which the field was set
302 // to true for async evaluating modules.
304 // This is arranged by using an integer to record the order. After evaluation is
305 // complete the value is set to ASYNC_EVALUATING_POST_ORDER_CLEARED.
307 // See https://tc39.es/ecma262/#sec-cyclic-module-records for field defintion.
308 // See https://tc39.es/ecma262/#sec-async-module-execution-fulfilled for sort
311 // Initial value for the runtime's counter used to generate these values.
312 constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_INIT
= 1;
314 // Value that the field is set to after being cleared.
315 constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_CLEARED
= 0;
317 // Currently, the ModuleObject class is used to represent both the Source Text
318 // Module Record and the Synthetic Module Record. Ideally, this is something
319 // that should be refactored to follow the same hierarchy as in the spec.
320 // TODO: See Bug 1880519.
321 class ModuleObject
: public NativeObject
{
323 // Module fields including those for AbstractModuleRecords described by:
324 // https://tc39.es/ecma262/#sec-abstract-module-records
329 CyclicModuleFieldsSlot
,
330 // `SyntheticModuleFields` if a synthetic module. Otherwise `undefined`.
331 SyntheticModuleFieldsSlot
,
335 static const JSClass class_
;
337 static bool isInstance(HandleValue value
);
339 static ModuleObject
* create(JSContext
* cx
);
341 static ModuleObject
* createSynthetic(
342 JSContext
* cx
, MutableHandle
<ExportNameVector
> exportNames
);
344 // Initialize the slots on this object that are dependent on the script.
345 void initScriptSlots(HandleScript script
);
347 void setInitialEnvironment(
348 Handle
<ModuleEnvironmentObject
*> initialEnvironment
);
350 void initFunctionDeclarations(UniquePtr
<FunctionDeclarationVector
> decls
);
351 void initImportExportData(
352 MutableHandle
<RequestedModuleVector
> requestedModules
,
353 MutableHandle
<ImportEntryVector
> importEntries
,
354 MutableHandle
<ExportEntryVector
> exportEntries
, uint32_t localExportCount
,
355 uint32_t indirectExportCount
, uint32_t starExportCount
);
356 static bool Freeze(JSContext
* cx
, Handle
<ModuleObject
*> self
);
358 static bool AssertFrozen(JSContext
* cx
, Handle
<ModuleObject
*> self
);
361 JSScript
* maybeScript() const;
362 JSScript
* script() const;
363 const char* filename() const;
364 ModuleEnvironmentObject
& initialEnvironment() const;
365 ModuleEnvironmentObject
* environment() const;
366 ModuleNamespaceObject
* namespace_();
367 ModuleStatus
status() const;
368 mozilla::Maybe
<uint32_t> maybeDfsIndex() const;
369 uint32_t dfsIndex() const;
370 mozilla::Maybe
<uint32_t> maybeDfsAncestorIndex() const;
371 uint32_t dfsAncestorIndex() const;
372 bool hadEvaluationError() const;
373 Value
maybeEvaluationError() const;
374 Value
evaluationError() const;
375 JSObject
* metaObject() const;
376 ScriptSourceObject
* scriptSourceObject() const;
377 mozilla::Span
<const RequestedModule
> requestedModules() const;
378 mozilla::Span
<const ImportEntry
> importEntries() const;
379 mozilla::Span
<const ExportEntry
> localExportEntries() const;
380 mozilla::Span
<const ExportEntry
> indirectExportEntries() const;
381 mozilla::Span
<const ExportEntry
> starExportEntries() const;
382 const ExportNameVector
& syntheticExportNames() const;
384 IndirectBindingMap
& importBindings();
386 void setStatus(ModuleStatus newStatus
);
387 void setDfsIndex(uint32_t index
);
388 void setDfsAncestorIndex(uint32_t index
);
389 void clearDfsIndexes();
391 static PromiseObject
* createTopLevelCapability(JSContext
* cx
,
392 Handle
<ModuleObject
*> module
);
393 bool hasTopLevelAwait() const;
394 bool isAsyncEvaluating() const;
395 void setAsyncEvaluating();
396 void setEvaluationError(HandleValue newValue
);
397 void setPendingAsyncDependencies(uint32_t newValue
);
398 void setInitialTopLevelCapability(Handle
<PromiseObject
*> capability
);
399 bool hasTopLevelCapability() const;
400 PromiseObject
* maybeTopLevelCapability() const;
401 PromiseObject
* topLevelCapability() const;
402 ListObject
* asyncParentModules() const;
403 mozilla::Maybe
<uint32_t> maybePendingAsyncDependencies() const;
404 uint32_t pendingAsyncDependencies() const;
405 mozilla::Maybe
<uint32_t> maybeAsyncEvaluatingPostOrder() const;
406 uint32_t getAsyncEvaluatingPostOrder() const;
407 void clearAsyncEvaluatingPostOrder();
408 void setCycleRoot(ModuleObject
* cycleRoot
);
409 ModuleObject
* getCycleRoot() const;
410 bool hasCyclicModuleFields() const;
411 bool hasSyntheticModuleFields() const;
413 static void onTopLevelEvaluationFinished(ModuleObject
* module
);
415 static bool appendAsyncParentModule(JSContext
* cx
, Handle
<ModuleObject
*> self
,
416 Handle
<ModuleObject
*> parent
);
418 [[nodiscard
]] static bool topLevelCapabilityResolve(
419 JSContext
* cx
, Handle
<ModuleObject
*> module
);
420 [[nodiscard
]] static bool topLevelCapabilityReject(
421 JSContext
* cx
, Handle
<ModuleObject
*> module
, HandleValue error
);
423 void setMetaObject(JSObject
* obj
);
425 static bool instantiateFunctionDeclarations(JSContext
* cx
,
426 Handle
<ModuleObject
*> self
);
428 static bool execute(JSContext
* cx
, Handle
<ModuleObject
*> self
);
430 static ModuleNamespaceObject
* createNamespace(
431 JSContext
* cx
, Handle
<ModuleObject
*> self
,
432 MutableHandle
<UniquePtr
<ExportNameVector
>> exports
);
434 static bool createEnvironment(JSContext
* cx
, Handle
<ModuleObject
*> self
);
435 static bool createSyntheticEnvironment(JSContext
* cx
,
436 Handle
<ModuleObject
*> self
,
437 Handle
<GCVector
<Value
>> values
);
439 void initAsyncSlots(JSContext
* cx
, bool hasTopLevelAwait
,
440 Handle
<ListObject
*> asyncParentModules
);
443 static const JSClassOps classOps_
;
445 static void trace(JSTracer
* trc
, JSObject
* obj
);
446 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
);
448 CyclicModuleFields
* cyclicModuleFields();
449 const CyclicModuleFields
* cyclicModuleFields() const;
451 SyntheticModuleFields
* syntheticModuleFields();
452 const SyntheticModuleFields
* syntheticModuleFields() const;
455 JSObject
* GetOrCreateModuleMetaObject(JSContext
* cx
, HandleObject module
);
457 ModuleObject
* CallModuleResolveHook(JSContext
* cx
,
458 HandleValue referencingPrivate
,
459 HandleObject moduleRequest
);
461 JSObject
* StartDynamicModuleImport(JSContext
* cx
, HandleScript script
,
462 HandleValue specifier
, HandleValue options
);
464 bool OnModuleEvaluationFailure(JSContext
* cx
, HandleObject evaluationPromise
,
465 JS::ModuleErrorBehaviour errorBehaviour
);
467 bool FinishDynamicModuleImport(JSContext
* cx
, HandleObject evaluationPromise
,
468 HandleValue referencingPrivate
,
469 HandleObject moduleRequest
,
470 HandleObject promise
);
475 inline bool JSObject::is
<js::ModuleNamespaceObject
>() const {
476 return js::IsDerivedProxyObject(this,
477 &js::ModuleNamespaceObject::proxyHandler
);
480 #endif /* builtin_ModuleObject_h */