ModuleObject.h (21031B)
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/. */ 6 7 #ifndef builtin_ModuleObject_h 8 #define builtin_ModuleObject_h 9 10 #include "mozilla/HashTable.h" // mozilla::{HashMap, DefaultHasher} 11 #include "mozilla/Maybe.h" // mozilla::Maybe 12 #include "mozilla/Span.h" 13 14 #include <cstdint> // UINT32_MAX 15 #include <stddef.h> // size_t 16 #include <stdint.h> // int32_t, uint32_t 17 18 #include "gc/Barrier.h" // HeapPtr 19 #include "gc/ZoneAllocator.h" // CellAllocPolicy 20 #include "js/Class.h" // JSClass, ObjectOpResult 21 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin 22 #include "js/GCVector.h" 23 #include "js/Id.h" // jsid 24 #include "js/Modules.h" 25 #include "js/Proxy.h" // BaseProxyHandler 26 #include "js/RootingAPI.h" // Rooted, Handle, MutableHandle 27 #include "js/TypeDecls.h" // HandleValue, HandleId, HandleObject, HandleScript, MutableHandleValue, MutableHandleIdVector, MutableHandleObject 28 #include "js/UniquePtr.h" // UniquePtr 29 #include "vm/JSObject.h" // JSObject 30 #include "vm/NativeObject.h" // NativeObject 31 #include "vm/ProxyObject.h" // ProxyObject 32 #include "vm/SharedStencil.h" // FunctionDeclarationVector 33 34 class JSAtom; 35 class JSScript; 36 class JSTracer; 37 38 namespace JS { 39 class PropertyDescriptor; 40 class Value; 41 } // namespace JS 42 43 namespace js { 44 45 class ArrayObject; 46 class CyclicModuleFields; 47 class SyntheticModuleFields; 48 class ListObject; 49 class ModuleEnvironmentObject; 50 class ModuleObject; 51 class PromiseObject; 52 class ScriptSourceObject; 53 54 class ImportAttribute { 55 const HeapPtr<JSAtom*> key_; 56 const HeapPtr<JSString*> value_; 57 58 public: 59 ImportAttribute(Handle<JSAtom*> key, Handle<JSString*> value); 60 61 JSAtom* key() const { return key_; } 62 JSString* value() const { return value_; } 63 64 void trace(JSTracer* trc); 65 }; 66 67 using ImportAttributeVector = GCVector<ImportAttribute, 0, SystemAllocPolicy>; 68 69 enum class ImportPhase : uint8_t { Evaluation, Limit }; 70 71 class ModuleRequestObject : public NativeObject { 72 public: 73 enum { 74 SpecifierSlot = 0, 75 FirstUnsupportedAttributeKeySlot, 76 ModuleTypeSlot, 77 PhaseSlot, 78 SlotCount 79 }; 80 81 static const JSClass class_; 82 static bool isInstance(HandleValue value); 83 [[nodiscard]] static ModuleRequestObject* create( 84 JSContext* cx, Handle<JSAtom*> specifier, 85 Handle<ImportAttributeVector> maybeAttributes, 86 ImportPhase phase = ImportPhase::Evaluation); 87 [[nodiscard]] static ModuleRequestObject* create( 88 JSContext* cx, Handle<JSAtom*> specifier, JS::ModuleType moduleType, 89 ImportPhase phase = ImportPhase::Evaluation); 90 91 JSAtom* specifier() const; 92 JS::ModuleType moduleType() const; 93 ImportPhase phase() const; 94 95 // We process import attributes earlier in the process, but according to the 96 // spec, we should error during module evaluation if we encounter an 97 // unsupported attribute. We want to generate a nice error message, so we need 98 // to keep track of the first unsupported key we encounter. 99 void setFirstUnsupportedAttributeKey(Handle<JSAtom*> key); 100 bool hasFirstUnsupportedAttributeKey() const; 101 JSAtom* getFirstUnsupportedAttributeKey() const; 102 }; 103 104 using ModuleRequestVector = 105 GCVector<HeapPtr<ModuleRequestObject*>, 0, SystemAllocPolicy>; 106 107 class ImportEntry { 108 const HeapPtr<ModuleRequestObject*> moduleRequest_; 109 const HeapPtr<JSAtom*> importName_; 110 const HeapPtr<JSAtom*> localName_; 111 112 // Line number (1-origin). 113 const uint32_t lineNumber_; 114 115 // Column number in UTF-16 code units. 116 const JS::ColumnNumberOneOrigin columnNumber_; 117 118 public: 119 ImportEntry(Handle<ModuleRequestObject*> moduleRequest, 120 Handle<JSAtom*> maybeImportName, Handle<JSAtom*> localName, 121 uint32_t lineNumber, JS::ColumnNumberOneOrigin columnNumber); 122 123 ModuleRequestObject* moduleRequest() const { return moduleRequest_; } 124 JSAtom* importName() const { return importName_; } 125 JSAtom* localName() const { return localName_; } 126 uint32_t lineNumber() const { return lineNumber_; } 127 JS::ColumnNumberOneOrigin columnNumber() const { return columnNumber_; } 128 129 void trace(JSTracer* trc); 130 }; 131 132 using ImportEntryVector = GCVector<ImportEntry, 0, SystemAllocPolicy>; 133 134 class ExportEntry { 135 const HeapPtr<JSAtom*> exportName_; 136 const HeapPtr<ModuleRequestObject*> moduleRequest_; 137 const HeapPtr<JSAtom*> importName_; 138 const HeapPtr<JSAtom*> localName_; 139 140 // Line number (1-origin). 141 const uint32_t lineNumber_; 142 143 // Column number in UTF-16 code units. 144 const JS::ColumnNumberOneOrigin columnNumber_; 145 146 public: 147 ExportEntry(Handle<JSAtom*> maybeExportName, 148 Handle<ModuleRequestObject*> maybeModuleRequest, 149 Handle<JSAtom*> maybeImportName, Handle<JSAtom*> maybeLocalName, 150 uint32_t lineNumber, JS::ColumnNumberOneOrigin columnNumber); 151 JSAtom* exportName() const { return exportName_; } 152 ModuleRequestObject* moduleRequest() const { return moduleRequest_; } 153 JSAtom* importName() const { return importName_; } 154 JSAtom* localName() const { return localName_; } 155 uint32_t lineNumber() const { return lineNumber_; } 156 JS::ColumnNumberOneOrigin columnNumber() const { return columnNumber_; } 157 158 void trace(JSTracer* trc); 159 }; 160 161 using ExportEntryVector = GCVector<ExportEntry, 0, SystemAllocPolicy>; 162 163 class RequestedModule { 164 const HeapPtr<ModuleRequestObject*> moduleRequest_; 165 166 // Line number (1-origin). 167 const uint32_t lineNumber_; 168 169 // Column number in UTF-16 code units. 170 const JS::ColumnNumberOneOrigin columnNumber_; 171 172 public: 173 RequestedModule(Handle<ModuleRequestObject*> moduleRequest, 174 uint32_t lineNumber, JS::ColumnNumberOneOrigin columnNumber); 175 ModuleRequestObject* moduleRequest() const { return moduleRequest_; } 176 uint32_t lineNumber() const { return lineNumber_; } 177 JS::ColumnNumberOneOrigin columnNumber() const { return columnNumber_; } 178 179 void trace(JSTracer* trc); 180 }; 181 182 using RequestedModuleVector = GCVector<RequestedModule, 0, SystemAllocPolicy>; 183 184 class ResolvedBindingObject : public NativeObject { 185 public: 186 enum { ModuleSlot = 0, BindingNameSlot, SlotCount }; 187 188 static const JSClass class_; 189 static bool isInstance(HandleValue value); 190 static ResolvedBindingObject* create(JSContext* cx, 191 Handle<ModuleObject*> module, 192 Handle<JSAtom*> bindingName); 193 ModuleObject* module() const; 194 JSAtom* bindingName() const; 195 }; 196 197 class IndirectBindingMap { 198 public: 199 void trace(JSTracer* trc); 200 201 bool put(JSContext* cx, HandleId name, 202 Handle<ModuleEnvironmentObject*> environment, HandleId targetName); 203 204 size_t count() const { return map_ ? map_->count() : 0; } 205 206 bool has(jsid name) const { return map_ ? map_->has(name) : false; } 207 208 bool lookup(jsid name, ModuleEnvironmentObject** envOut, 209 mozilla::Maybe<PropertyInfo>* propOut) const; 210 211 template <typename Func> 212 void forEachExportedName(Func func) const { 213 if (!map_) { 214 return; 215 } 216 217 for (auto r = map_->all(); !r.empty(); r.popFront()) { 218 func(r.front().key()); 219 } 220 } 221 222 private: 223 struct Binding { 224 Binding(ModuleEnvironmentObject* environment, jsid targetName, 225 PropertyInfo prop); 226 HeapPtr<ModuleEnvironmentObject*> environment; 227 #ifdef DEBUG 228 HeapPtr<jsid> targetName; 229 #endif 230 PropertyInfo prop; 231 }; 232 233 using Map = mozilla::HashMap<PreBarriered<jsid>, Binding, 234 mozilla::DefaultHasher<PreBarriered<jsid>>, 235 CellAllocPolicy>; 236 237 mozilla::Maybe<Map> map_; 238 }; 239 240 // Vector of atoms representing the names exported from a module namespace. 241 // 242 // This is used both on the stack and in the heap. 243 using ExportNameVector = GCVector<HeapPtr<JSAtom*>, 0, SystemAllocPolicy>; 244 245 class ModuleNamespaceObject : public ProxyObject { 246 public: 247 enum ModuleNamespaceSlot { ExportsSlot = 0, BindingsSlot }; 248 249 static bool isInstance(HandleValue value); 250 static ModuleNamespaceObject* create( 251 JSContext* cx, Handle<ModuleObject*> module, 252 MutableHandle<UniquePtr<ExportNameVector>> exports, 253 MutableHandle<UniquePtr<IndirectBindingMap>> bindings); 254 255 ModuleObject& module(); 256 const ExportNameVector& exports() const; 257 IndirectBindingMap& bindings(); 258 259 bool addBinding(JSContext* cx, Handle<JSAtom*> exportedName, 260 Handle<ModuleObject*> targetModule, 261 Handle<JSAtom*> targetName); 262 263 private: 264 struct ProxyHandler : public BaseProxyHandler { 265 constexpr ProxyHandler() : BaseProxyHandler(&family, false) {} 266 267 bool getOwnPropertyDescriptor( 268 JSContext* cx, HandleObject proxy, HandleId id, 269 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const override; 270 bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, 271 Handle<PropertyDescriptor> desc, 272 ObjectOpResult& result) const override; 273 bool ownPropertyKeys(JSContext* cx, HandleObject proxy, 274 MutableHandleIdVector props) const override; 275 bool delete_(JSContext* cx, HandleObject proxy, HandleId id, 276 ObjectOpResult& result) const override; 277 bool getPrototype(JSContext* cx, HandleObject proxy, 278 MutableHandleObject protop) const override; 279 bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, 280 ObjectOpResult& result) const override; 281 bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, 282 bool* isOrdinary, 283 MutableHandleObject protop) const override; 284 bool setImmutablePrototype(JSContext* cx, HandleObject proxy, 285 bool* succeeded) const override; 286 287 bool preventExtensions(JSContext* cx, HandleObject proxy, 288 ObjectOpResult& result) const override; 289 bool isExtensible(JSContext* cx, HandleObject proxy, 290 bool* extensible) const override; 291 bool has(JSContext* cx, HandleObject proxy, HandleId id, 292 bool* bp) const override; 293 bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, 294 HandleId id, MutableHandleValue vp) const override; 295 bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, 296 HandleValue receiver, ObjectOpResult& result) const override; 297 298 void trace(JSTracer* trc, JSObject* proxy) const override; 299 void finalize(JS::GCContext* gcx, JSObject* proxy) const override; 300 301 static const char family; 302 }; 303 304 bool hasBindings() const; 305 bool hasExports() const; 306 307 ExportNameVector& mutableExports(); 308 309 public: 310 static const ProxyHandler proxyHandler; 311 }; 312 313 // Value types of [[Status]] in a Cyclic Module Record 314 // https://tc39.es/ecma262/#table-cyclic-module-fields 315 enum class ModuleStatus : int8_t { 316 New, 317 Unlinked, 318 Linking, 319 Linked, 320 Evaluating, 321 EvaluatingAsync, 322 Evaluated, 323 324 // Sub-state of Evaluated with error value set. 325 // 326 // This is not returned from ModuleObject::status(); use hadEvaluationError() 327 // to check this. 328 Evaluated_Error 329 }; 330 331 // Special values for CyclicModuleFields' asyncEvaluationOrderSlot field, 332 // which represents the AsyncEvaluationOrder field of cyclic module records. 333 // 334 // AsyncEvaluationOrder can have three states: 335 // - a positive integer, represented by values <= 336 // ASYNC_EVALUATING_POST_ORDER_MAX_VALUE 337 // - ~unset~, represented by ASYNC_EVALUATING_POST_ORDER_UNSET 338 // - ~done~, represented by ASYNC_EVALUATING_POST_ORDER_DONE 339 // 340 // See https://tc39.es/ecma262/#sec-cyclic-module-records for field defintion. 341 // See https://tc39.es/ecma262/#sec-async-module-execution-fulfilled for sort 342 // requirement. 343 344 // Value that the field is initially set to. 345 constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_UNSET = UINT32_MAX; 346 347 // Value that the field is set to after being cleared. 348 constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_DONE = UINT32_MAX - 1; 349 350 constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_MAX_VALUE = UINT32_MAX - 2; 351 352 class AsyncEvaluationOrder { 353 private: 354 uint32_t value = ASYNC_EVALUATING_POST_ORDER_UNSET; 355 356 public: 357 bool isUnset() const; 358 bool isInteger() const; 359 bool isDone() const; 360 361 uint32_t get() const; 362 363 void set(JSRuntime* rt); 364 void setDone(JSRuntime* rt); 365 }; 366 367 // The map used by [[LoadedModules]] in Realm Record Fields, Script Record 368 // Fields, and additional fields of Cyclic Module Records. 369 // https://tc39.es/ecma262/#table-realm-record-fields 370 // https://tc39.es/ecma262/#table-script-records 371 // https://tc39.es/ecma262/#table-cyclic-module-fields 372 // 373 // For Import attributes proposal, this map maps from ModuleRequest records to 374 // Module records. 375 // https://tc39.es/proposal-import-attributes/#sec-cyclic-module-records 376 // 377 // TODO: 378 // Bug 1968874 : Implement [[LoadedModules]] in Realm Records and Script Records 379 using LoadedModuleMap = 380 GCHashMap<HeapPtr<JSObject*>, HeapPtr<ModuleObject*>, 381 StableCellHasher<HeapPtr<JSObject*>>, SystemAllocPolicy>; 382 383 // Currently, the ModuleObject class is used to represent both the Source Text 384 // Module Record and the Synthetic Module Record. Ideally, this is something 385 // that should be refactored to follow the same hierarchy as in the spec. 386 // TODO: See Bug 1880519. 387 class ModuleObject : public NativeObject { 388 public: 389 // Module fields including those for AbstractModuleRecords described by: 390 // https://tc39.es/ecma262/#sec-abstract-module-records 391 enum ModuleSlot { 392 ScriptSlot = 0, 393 EnvironmentSlot, 394 NamespaceSlot, 395 CyclicModuleFieldsSlot, 396 // `SyntheticModuleFields` if a synthetic module. Otherwise `undefined`. 397 SyntheticModuleFieldsSlot, 398 SlotCount 399 }; 400 401 static const JSClass class_; 402 403 static bool isInstance(HandleValue value); 404 405 static ModuleObject* create(JSContext* cx); 406 407 static ModuleObject* createSynthetic( 408 JSContext* cx, MutableHandle<ExportNameVector> exportNames); 409 410 // Initialize the slots on this object that are dependent on the script. 411 void initScriptSlots(HandleScript script); 412 413 void setInitialEnvironment( 414 Handle<ModuleEnvironmentObject*> initialEnvironment); 415 416 void initFunctionDeclarations(UniquePtr<FunctionDeclarationVector> decls); 417 void initImportExportData( 418 MutableHandle<RequestedModuleVector> requestedModules, 419 MutableHandle<ImportEntryVector> importEntries, 420 MutableHandle<ExportEntryVector> exportEntries, uint32_t localExportCount, 421 uint32_t indirectExportCount, uint32_t starExportCount); 422 static bool Freeze(JSContext* cx, Handle<ModuleObject*> self); 423 #ifdef DEBUG 424 static bool AssertFrozen(JSContext* cx, Handle<ModuleObject*> self); 425 #endif 426 427 JSScript* maybeScript() const; 428 JSScript* script() const; 429 const char* filename() const; 430 ModuleEnvironmentObject& initialEnvironment() const; 431 ModuleEnvironmentObject* environment() const; 432 ModuleNamespaceObject* namespace_(); 433 ModuleStatus status() const; 434 mozilla::Maybe<uint32_t> maybeDfsAncestorIndex() const; 435 uint32_t dfsAncestorIndex() const; 436 bool hadEvaluationError() const; 437 Value maybeEvaluationError() const; 438 Value evaluationError() const; 439 JSObject* metaObject() const; 440 ScriptSourceObject* scriptSourceObject() const; 441 mozilla::Span<const RequestedModule> requestedModules() const; 442 mozilla::Span<const ImportEntry> importEntries() const; 443 mozilla::Span<const ExportEntry> localExportEntries() const; 444 mozilla::Span<const ExportEntry> indirectExportEntries() const; 445 mozilla::Span<const ExportEntry> starExportEntries() const; 446 const ExportNameVector& syntheticExportNames() const; 447 448 IndirectBindingMap& importBindings(); 449 450 void setStatus(ModuleStatus newStatus); 451 void setDfsAncestorIndex(uint32_t index); 452 void clearDfsAncestorIndex(); 453 454 static PromiseObject* createTopLevelCapability(JSContext* cx, 455 Handle<ModuleObject*> module); 456 bool hasTopLevelAwait() const; 457 void setEvaluationError(HandleValue newValue); 458 void setPendingAsyncDependencies(uint32_t newValue); 459 void setInitialTopLevelCapability(Handle<PromiseObject*> capability); 460 bool hasTopLevelCapability() const; 461 PromiseObject* maybeTopLevelCapability() const; 462 PromiseObject* topLevelCapability() const; 463 ListObject* asyncParentModules() const; 464 mozilla::Maybe<uint32_t> maybePendingAsyncDependencies() const; 465 uint32_t pendingAsyncDependencies() const; 466 AsyncEvaluationOrder& asyncEvaluationOrder(); 467 AsyncEvaluationOrder const& asyncEvaluationOrder() const; 468 void setCycleRoot(ModuleObject* cycleRoot); 469 ModuleObject* getCycleRoot() const; 470 bool hasCyclicModuleFields() const; 471 bool hasSyntheticModuleFields() const; 472 LoadedModuleMap& loadedModules(); 473 const LoadedModuleMap& loadedModules() const; 474 475 static void onTopLevelEvaluationFinished(ModuleObject* module); 476 477 static bool appendAsyncParentModule(JSContext* cx, Handle<ModuleObject*> self, 478 Handle<ModuleObject*> parent); 479 480 [[nodiscard]] static bool topLevelCapabilityResolve( 481 JSContext* cx, Handle<ModuleObject*> module); 482 [[nodiscard]] static bool topLevelCapabilityReject( 483 JSContext* cx, Handle<ModuleObject*> module, HandleValue error); 484 485 void setMetaObject(JSObject* obj); 486 487 static bool instantiateFunctionDeclarations(JSContext* cx, 488 Handle<ModuleObject*> self); 489 490 static bool execute(JSContext* cx, Handle<ModuleObject*> self); 491 492 static ModuleNamespaceObject* createNamespace( 493 JSContext* cx, Handle<ModuleObject*> self, 494 MutableHandle<UniquePtr<ExportNameVector>> exports); 495 496 static bool createEnvironment(JSContext* cx, Handle<ModuleObject*> self); 497 static bool createSyntheticEnvironment(JSContext* cx, 498 Handle<ModuleObject*> self, 499 JS::HandleVector<Value> values); 500 501 void initAsyncSlots(JSContext* cx, bool hasTopLevelAwait, 502 Handle<ListObject*> asyncParentModules); 503 504 private: 505 static const JSClassOps classOps_; 506 507 static void trace(JSTracer* trc, JSObject* obj); 508 static void finalize(JS::GCContext* gcx, JSObject* obj); 509 510 CyclicModuleFields* cyclicModuleFields(); 511 const CyclicModuleFields* cyclicModuleFields() const; 512 513 SyntheticModuleFields* syntheticModuleFields(); 514 const SyntheticModuleFields* syntheticModuleFields() const; 515 }; 516 517 using VisitedModuleSet = 518 GCHashSet<HeapPtr<ModuleObject*>, DefaultHasher<HeapPtr<ModuleObject*>>, 519 SystemAllocPolicy>; 520 521 // The fields of a GraphLoadingState Record, as described in: 522 // https://tc39.es/ecma262/#graphloadingstate-record 523 struct GraphLoadingStateRecord { 524 GraphLoadingStateRecord() = default; 525 GraphLoadingStateRecord(JS::LoadModuleResolvedCallback resolved, 526 JS::LoadModuleRejectedCallback rejected); 527 528 void trace(JSTracer* trc); 529 530 // [[Visited]] : a List of Cyclic Module Records 531 VisitedModuleSet visited; 532 533 JS::LoadModuleResolvedCallback resolved; 534 JS::LoadModuleRejectedCallback rejected; 535 }; 536 537 class GraphLoadingStateRecordObject : public NativeObject { 538 public: 539 enum { 540 StateSlot = 0, 541 PromiseSlot, 542 IsLoadingSlot, 543 PendingModulesCountSlot, 544 HostDefinedSlot, 545 SlotCount 546 }; 547 548 static const JSClass class_; 549 static const JSClassOps classOps_; 550 551 [[nodiscard]] static GraphLoadingStateRecordObject* create( 552 JSContext* cx, bool isLoading, uint32_t pendingModulesCount, 553 JS::LoadModuleResolvedCallback resolved, 554 JS::LoadModuleRejectedCallback rejected, Handle<Value> hostDefined); 555 556 [[nodiscard]] static GraphLoadingStateRecordObject* create( 557 JSContext* cx, bool isLoading, uint32_t pendingModulesCount, 558 Handle<PromiseObject*> promise, Handle<Value> hostDefined); 559 560 static void finalize(JS::GCContext* gcx, JSObject* obj); 561 static void trace(JSTracer* trc, JSObject* obj); 562 563 // [[PromiseCapability]] : a PromiseCapability Record 564 PromiseObject* promise(); 565 566 // [[IsLoading]] : a Boolean 567 bool isLoading(); 568 void setIsLoading(bool isLoading); 569 570 // [[PendingModulesCount]] : a non-negative integer 571 uint32_t pendingModulesCount(); 572 void setPendingModulesCount(uint32_t count); 573 574 VisitedModuleSet& visited(); 575 576 Value hostDefined(); 577 578 bool resolved(JSContext* cx, JS::Handle<JS::Value> hostDefined); 579 bool rejected(JSContext* cx, JS::Handle<JS::Value> hostDefined, 580 Handle<JS::Value> error); 581 }; 582 583 JSObject* GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module); 584 585 JSObject* StartDynamicModuleImport(JSContext* cx, HandleScript script, 586 HandleValue specifier, HandleValue options); 587 588 } // namespace js 589 590 template <> 591 inline bool JSObject::is<js::ModuleNamespaceObject>() const { 592 return js::IsDerivedProxyObject(this, 593 &js::ModuleNamespaceObject::proxyHandler); 594 } 595 596 #endif /* builtin_ModuleObject_h */