summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/ModuleObject.h
blob: d39d65b18c5c8a51e0abec757cc2d2b91c118657 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef builtin_ModuleObject_h
#define builtin_ModuleObject_h

#include "mozilla/HashTable.h"  // mozilla::{HashMap, DefaultHasher}
#include "mozilla/Maybe.h"      // mozilla::Maybe
#include "mozilla/Span.h"

#include <stddef.h>  // size_t
#include <stdint.h>  // int32_t, uint32_t

#include "gc/Barrier.h"        // HeapPtr
#include "gc/ZoneAllocator.h"  // CellAllocPolicy
#include "js/Class.h"          // JSClass, ObjectOpResult
#include "js/ColumnNumber.h"   // JS::ColumnNumberOneOrigin
#include "js/GCVector.h"
#include "js/Id.h"  // jsid
#include "js/Modules.h"
#include "js/Proxy.h"       // BaseProxyHandler
#include "js/RootingAPI.h"  // Rooted, Handle, MutableHandle
#include "js/TypeDecls.h"  // HandleValue, HandleId, HandleObject, HandleScript, MutableHandleValue, MutableHandleIdVector, MutableHandleObject
#include "js/UniquePtr.h"  // UniquePtr
#include "vm/JSObject.h"   // JSObject
#include "vm/NativeObject.h"   // NativeObject
#include "vm/ProxyObject.h"    // ProxyObject
#include "vm/SharedStencil.h"  // FunctionDeclarationVector

class JSAtom;
class JSScript;
class JSTracer;

namespace JS {
class PropertyDescriptor;
class Value;
}  // namespace JS

namespace js {

class ArrayObject;
class CyclicModuleFields;
class SyntheticModuleFields;
class ListObject;
class ModuleEnvironmentObject;
class ModuleObject;
class PromiseObject;
class ScriptSourceObject;

class ModuleRequestObject : public NativeObject {
 public:
  enum { SpecifierSlot = 0, AttributesSlot, SlotCount };

  static const JSClass class_;
  static bool isInstance(HandleValue value);
  [[nodiscard]] static ModuleRequestObject* create(
      JSContext* cx, Handle<JSAtom*> specifier,
      Handle<ArrayObject*> maybeAttributes);

  JSAtom* specifier() const;
  ArrayObject* attributes() const;
  bool hasAttributes() const;
  static bool getModuleType(JSContext* cx,
                            const Handle<ModuleRequestObject*> moduleRequest,
                            JS::ModuleType& moduleType);
};

using ModuleRequestVector =
    GCVector<HeapPtr<ModuleRequestObject*>, 0, SystemAllocPolicy>;

class ImportEntry {
  const HeapPtr<ModuleRequestObject*> moduleRequest_;
  const HeapPtr<JSAtom*> importName_;
  const HeapPtr<JSAtom*> localName_;

  // Line number (1-origin).
  const uint32_t lineNumber_;

  // Column number in UTF-16 code units.
  const JS::ColumnNumberOneOrigin columnNumber_;

 public:
  ImportEntry(Handle<ModuleRequestObject*> moduleRequest,
              Handle<JSAtom*> maybeImportName, Handle<JSAtom*> localName,
              uint32_t lineNumber, JS::ColumnNumberOneOrigin columnNumber);

  ModuleRequestObject* moduleRequest() const { return moduleRequest_; }
  JSAtom* importName() const { return importName_; }
  JSAtom* localName() const { return localName_; }
  uint32_t lineNumber() const { return lineNumber_; }
  JS::ColumnNumberOneOrigin columnNumber() const { return columnNumber_; }

  void trace(JSTracer* trc);
};

using ImportEntryVector = GCVector<ImportEntry, 0, SystemAllocPolicy>;

class ExportEntry {
  const HeapPtr<JSAtom*> exportName_;
  const HeapPtr<ModuleRequestObject*> moduleRequest_;
  const HeapPtr<JSAtom*> importName_;
  const HeapPtr<JSAtom*> localName_;

  // Line number (1-origin).
  const uint32_t lineNumber_;

  // Column number in UTF-16 code units.
  const JS::ColumnNumberOneOrigin columnNumber_;

 public:
  ExportEntry(Handle<JSAtom*> maybeExportName,
              Handle<ModuleRequestObject*> maybeModuleRequest,
              Handle<JSAtom*> maybeImportName, Handle<JSAtom*> maybeLocalName,
              uint32_t lineNumber, JS::ColumnNumberOneOrigin columnNumber);
  JSAtom* exportName() const { return exportName_; }
  ModuleRequestObject* moduleRequest() const { return moduleRequest_; }
  JSAtom* importName() const { return importName_; }
  JSAtom* localName() const { return localName_; }
  uint32_t lineNumber() const { return lineNumber_; }
  JS::ColumnNumberOneOrigin columnNumber() const { return columnNumber_; }

  void trace(JSTracer* trc);
};

using ExportEntryVector = GCVector<ExportEntry, 0, SystemAllocPolicy>;

class RequestedModule {
  const HeapPtr<ModuleRequestObject*> moduleRequest_;

  // Line number (1-origin).
  const uint32_t lineNumber_;

  // Column number in UTF-16 code units.
  const JS::ColumnNumberOneOrigin columnNumber_;

 public:
  RequestedModule(Handle<ModuleRequestObject*> moduleRequest,
                  uint32_t lineNumber, JS::ColumnNumberOneOrigin columnNumber);
  ModuleRequestObject* moduleRequest() const { return moduleRequest_; }
  uint32_t lineNumber() const { return lineNumber_; }
  JS::ColumnNumberOneOrigin columnNumber() const { return columnNumber_; }

  void trace(JSTracer* trc);
};

using RequestedModuleVector = GCVector<RequestedModule, 0, SystemAllocPolicy>;

class ResolvedBindingObject : public NativeObject {
 public:
  enum { ModuleSlot = 0, BindingNameSlot, SlotCount };

  static const JSClass class_;
  static bool isInstance(HandleValue value);
  static ResolvedBindingObject* create(JSContext* cx,
                                       Handle<ModuleObject*> module,
                                       Handle<JSAtom*> bindingName);
  ModuleObject* module() const;
  JSAtom* bindingName() const;
};

class IndirectBindingMap {
 public:
  void trace(JSTracer* trc);

  bool put(JSContext* cx, HandleId name,
           Handle<ModuleEnvironmentObject*> environment, HandleId targetName);

  size_t count() const { return map_ ? map_->count() : 0; }

  bool has(jsid name) const { return map_ ? map_->has(name) : false; }

  bool lookup(jsid name, ModuleEnvironmentObject** envOut,
              mozilla::Maybe<PropertyInfo>* propOut) const;

  template <typename Func>
  void forEachExportedName(Func func) const {
    if (!map_) {
      return;
    }

    for (auto r = map_->all(); !r.empty(); r.popFront()) {
      func(r.front().key());
    }
  }

 private:
  struct Binding {
    Binding(ModuleEnvironmentObject* environment, jsid targetName,
            PropertyInfo prop);
    HeapPtr<ModuleEnvironmentObject*> environment;
#ifdef DEBUG
    HeapPtr<jsid> targetName;
#endif
    PropertyInfo prop;
  };

  using Map = mozilla::HashMap<PreBarriered<jsid>, Binding,
                               mozilla::DefaultHasher<PreBarriered<jsid>>,
                               CellAllocPolicy>;

  mozilla::Maybe<Map> map_;
};

// Vector of atoms representing the names exported from a module namespace.
//
// This is used both on the stack and in the heap.
using ExportNameVector = GCVector<HeapPtr<JSAtom*>, 0, SystemAllocPolicy>;

class ModuleNamespaceObject : public ProxyObject {
 public:
  enum ModuleNamespaceSlot { ExportsSlot = 0, BindingsSlot };

  static bool isInstance(HandleValue value);
  static ModuleNamespaceObject* create(
      JSContext* cx, Handle<ModuleObject*> module,
      MutableHandle<UniquePtr<ExportNameVector>> exports,
      MutableHandle<UniquePtr<IndirectBindingMap>> bindings);

  ModuleObject& module();
  const ExportNameVector& exports() const;
  IndirectBindingMap& bindings();

  bool addBinding(JSContext* cx, Handle<JSAtom*> exportedName,
                  Handle<ModuleObject*> targetModule,
                  Handle<JSAtom*> targetName);

 private:
  struct ProxyHandler : public BaseProxyHandler {
    ProxyHandler();

    bool getOwnPropertyDescriptor(
        JSContext* cx, HandleObject proxy, HandleId id,
        MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const override;
    bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
                        Handle<PropertyDescriptor> desc,
                        ObjectOpResult& result) const override;
    bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
                         MutableHandleIdVector props) const override;
    bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
                 ObjectOpResult& result) const override;
    bool getPrototype(JSContext* cx, HandleObject proxy,
                      MutableHandleObject protop) const override;
    bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
                      ObjectOpResult& result) const override;
    bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy,
                                bool* isOrdinary,
                                MutableHandleObject protop) const override;
    bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
                               bool* succeeded) const override;

    bool preventExtensions(JSContext* cx, HandleObject proxy,
                           ObjectOpResult& result) const override;
    bool isExtensible(JSContext* cx, HandleObject proxy,
                      bool* extensible) const override;
    bool has(JSContext* cx, HandleObject proxy, HandleId id,
             bool* bp) const override;
    bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
             HandleId id, MutableHandleValue vp) const override;
    bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
             HandleValue receiver, ObjectOpResult& result) const override;

    void trace(JSTracer* trc, JSObject* proxy) const override;
    void finalize(JS::GCContext* gcx, JSObject* proxy) const override;

    static const char family;
  };

  bool hasBindings() const;
  bool hasExports() const;

  ExportNameVector& mutableExports();

 public:
  static const ProxyHandler proxyHandler;
};

// Value types of [[Status]] in a Cyclic Module Record
// https://tc39.es/ecma262/#table-cyclic-module-fields
enum class ModuleStatus : int8_t {
  Unlinked,
  Linking,
  Linked,
  Evaluating,
  EvaluatingAsync,
  Evaluated,

  // Sub-state of Evaluated with error value set.
  //
  // This is not returned from ModuleObject::status(); use hadEvaluationError()
  // to check this.
  Evaluated_Error
};

// Special values for CyclicModuleFields' asyncEvaluatingPostOrderSlot field,
// which is used as part of the implementation of the AsyncEvaluation field of
// cyclic module records.
//
// The spec requires us to be able to tell the order in which the field was set
// to true for async evaluating modules.
//
// This is arranged by using an integer to record the order. After evaluation is
// complete the value is set to ASYNC_EVALUATING_POST_ORDER_CLEARED.
//
// See https://tc39.es/ecma262/#sec-cyclic-module-records for field defintion.
// See https://tc39.es/ecma262/#sec-async-module-execution-fulfilled for sort
// requirement.

// Initial value for the runtime's counter used to generate these values.
constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_INIT = 1;

// Value that the field is set to after being cleared.
constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_CLEARED = 0;

// Currently, the ModuleObject class is used to represent both the Source Text
// Module Record and the Synthetic Module Record. Ideally, this is something
// that should be refactored to follow the same hierarchy as in the spec.
// TODO: See Bug 1880519.
class ModuleObject : public NativeObject {
 public:
  // Module fields including those for AbstractModuleRecords described by:
  // https://tc39.es/ecma262/#sec-abstract-module-records
  enum ModuleSlot {
    ScriptSlot = 0,
    EnvironmentSlot,
    NamespaceSlot,
    CyclicModuleFieldsSlot,
    // `SyntheticModuleFields` if a synthetic module. Otherwise `undefined`.
    SyntheticModuleFieldsSlot,
    SlotCount
  };

  static const JSClass class_;

  static bool isInstance(HandleValue value);

  static ModuleObject* create(JSContext* cx);

  static ModuleObject* createSynthetic(
      JSContext* cx, MutableHandle<ExportNameVector> exportNames);

  // Initialize the slots on this object that are dependent on the script.
  void initScriptSlots(HandleScript script);

  void setInitialEnvironment(
      Handle<ModuleEnvironmentObject*> initialEnvironment);

  void initFunctionDeclarations(UniquePtr<FunctionDeclarationVector> decls);
  void initImportExportData(
      MutableHandle<RequestedModuleVector> requestedModules,
      MutableHandle<ImportEntryVector> importEntries,
      MutableHandle<ExportEntryVector> exportEntries, uint32_t localExportCount,
      uint32_t indirectExportCount, uint32_t starExportCount);
  static bool Freeze(JSContext* cx, Handle<ModuleObject*> self);
#ifdef DEBUG
  static bool AssertFrozen(JSContext* cx, Handle<ModuleObject*> self);
#endif

  JSScript* maybeScript() const;
  JSScript* script() const;
  ModuleEnvironmentObject& initialEnvironment() const;
  ModuleEnvironmentObject* environment() const;
  ModuleNamespaceObject* namespace_();
  ModuleStatus status() const;
  mozilla::Maybe<uint32_t> maybeDfsIndex() const;
  uint32_t dfsIndex() const;
  mozilla::Maybe<uint32_t> maybeDfsAncestorIndex() const;
  uint32_t dfsAncestorIndex() const;
  bool hadEvaluationError() const;
  Value maybeEvaluationError() const;
  Value evaluationError() const;
  JSObject* metaObject() const;
  ScriptSourceObject* scriptSourceObject() const;
  mozilla::Span<const RequestedModule> requestedModules() const;
  mozilla::Span<const ImportEntry> importEntries() const;
  mozilla::Span<const ExportEntry> localExportEntries() const;
  mozilla::Span<const ExportEntry> indirectExportEntries() const;
  mozilla::Span<const ExportEntry> starExportEntries() const;
  const ExportNameVector& syntheticExportNames() const;

  IndirectBindingMap& importBindings();

  void setStatus(ModuleStatus newStatus);
  void setDfsIndex(uint32_t index);
  void setDfsAncestorIndex(uint32_t index);
  void clearDfsIndexes();

  static PromiseObject* createTopLevelCapability(JSContext* cx,
                                                 Handle<ModuleObject*> module);
  bool hasTopLevelAwait() const;
  bool isAsyncEvaluating() const;
  void setAsyncEvaluating();
  void setEvaluationError(HandleValue newValue);
  void setPendingAsyncDependencies(uint32_t newValue);
  void setInitialTopLevelCapability(Handle<PromiseObject*> capability);
  bool hasTopLevelCapability() const;
  PromiseObject* maybeTopLevelCapability() const;
  PromiseObject* topLevelCapability() const;
  ListObject* asyncParentModules() const;
  mozilla::Maybe<uint32_t> maybePendingAsyncDependencies() const;
  uint32_t pendingAsyncDependencies() const;
  mozilla::Maybe<uint32_t> maybeAsyncEvaluatingPostOrder() const;
  uint32_t getAsyncEvaluatingPostOrder() const;
  void clearAsyncEvaluatingPostOrder();
  void setCycleRoot(ModuleObject* cycleRoot);
  ModuleObject* getCycleRoot() const;
  bool hasCyclicModuleFields() const;
  bool hasSyntheticModuleFields() const;

  static void onTopLevelEvaluationFinished(ModuleObject* module);

  static bool appendAsyncParentModule(JSContext* cx, Handle<ModuleObject*> self,
                                      Handle<ModuleObject*> parent);

  [[nodiscard]] static bool topLevelCapabilityResolve(
      JSContext* cx, Handle<ModuleObject*> module);
  [[nodiscard]] static bool topLevelCapabilityReject(
      JSContext* cx, Handle<ModuleObject*> module, HandleValue error);

  void setMetaObject(JSObject* obj);

  static bool instantiateFunctionDeclarations(JSContext* cx,
                                              Handle<ModuleObject*> self);

  static bool execute(JSContext* cx, Handle<ModuleObject*> self);

  static ModuleNamespaceObject* createNamespace(
      JSContext* cx, Handle<ModuleObject*> self,
      MutableHandle<UniquePtr<ExportNameVector>> exports);

  static bool createEnvironment(JSContext* cx, Handle<ModuleObject*> self);
  static bool createSyntheticEnvironment(JSContext* cx,
                                         Handle<ModuleObject*> self,
                                         Handle<GCVector<Value>> values);

  void initAsyncSlots(JSContext* cx, bool hasTopLevelAwait,
                      Handle<ListObject*> asyncParentModules);

 private:
  static const JSClassOps classOps_;

  static void trace(JSTracer* trc, JSObject* obj);
  static void finalize(JS::GCContext* gcx, JSObject* obj);

  CyclicModuleFields* cyclicModuleFields();
  const CyclicModuleFields* cyclicModuleFields() const;

  SyntheticModuleFields* syntheticModuleFields();
  const SyntheticModuleFields* syntheticModuleFields() const;
};

JSObject* GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module);

ModuleObject* CallModuleResolveHook(JSContext* cx,
                                    HandleValue referencingPrivate,
                                    HandleObject moduleRequest);

JSObject* StartDynamicModuleImport(JSContext* cx, HandleScript script,
                                   HandleValue specifier, HandleValue options);

bool OnModuleEvaluationFailure(JSContext* cx, HandleObject evaluationPromise,
                               JS::ModuleErrorBehaviour errorBehaviour);

bool FinishDynamicModuleImport(JSContext* cx, HandleObject evaluationPromise,
                               HandleValue referencingPrivate,
                               HandleObject moduleRequest,
                               HandleObject promise);

}  // namespace js

template <>
inline bool JSObject::is<js::ModuleNamespaceObject>() const {
  return js::IsDerivedProxyObject(this,
                                  &js::ModuleNamespaceObject::proxyHandler);
}

#endif /* builtin_ModuleObject_h */