/* -*- 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 // size_t #include // int32_t, uint32_t #include "builtin/SelfHostingDefines.h" // MODULE_OBJECT_* #include "gc/Barrier.h" // HeapPtr, PreBarrieredId #include "gc/Rooting.h" // HandleAtom, HandleArrayObject #include "gc/ZoneAllocator.h" // ZoneAllocPolicy #include "js/Class.h" // JSClass, ObjectOpResult #include "js/GCVector.h" // GCVector #include "js/Id.h" // jsid #include "js/Modules.h" // JS::DynamicImportStatus #include "js/PropertyDescriptor.h" // PropertyDescriptor #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 "js/Value.h" // JS::Value #include "vm/JSAtom.h" // JSAtom #include "vm/JSObject.h" // JSObject #include "vm/List.h" // ListObject #include "vm/NativeObject.h" // NativeObject #include "vm/PromiseObject.h" // js::PromiseObject #include "vm/ProxyObject.h" // ProxyObject #include "vm/Xdr.h" // XDRMode, XDRResult, XDRState class JSFreeOp; class JSScript; class JSTracer; namespace js { class ArrayObject; class Shape; class Scope; class ScriptSourceObject; class ModuleEnvironmentObject; class ModuleObject; using RootedModuleObject = Rooted; using HandleModuleObject = Handle; using RootedModuleEnvironmentObject = Rooted; using HandleModuleEnvironmentObject = Handle; class ImportEntryObject : public NativeObject { public: enum { ModuleRequestSlot = 0, ImportNameSlot, LocalNameSlot, LineNumberSlot, ColumnNumberSlot, SlotCount }; static const JSClass class_; static bool isInstance(HandleValue value); static ImportEntryObject* create(JSContext* cx, HandleAtom moduleRequest, HandleAtom importName, HandleAtom localName, uint32_t lineNumber, uint32_t columnNumber); JSAtom* moduleRequest() const; JSAtom* importName() const; JSAtom* localName() const; uint32_t lineNumber() const; uint32_t columnNumber() const; }; using RootedImportEntryObject = Rooted; using HandleImportEntryObject = Handle; using RootedImportEntryVector = Rooted >; using MutableHandleImportEntryObject = MutableHandle; template XDRResult XDRImportEntryObject(XDRState* xdr, MutableHandleImportEntryObject impObj); class ExportEntryObject : public NativeObject { public: enum { ExportNameSlot = 0, ModuleRequestSlot, ImportNameSlot, LocalNameSlot, LineNumberSlot, ColumnNumberSlot, SlotCount }; static const JSClass class_; static bool isInstance(HandleValue value); static ExportEntryObject* create(JSContext* cx, HandleAtom maybeExportName, HandleAtom maybeModuleRequest, HandleAtom maybeImportName, HandleAtom maybeLocalName, uint32_t lineNumber, uint32_t columnNumber); JSAtom* exportName() const; JSAtom* moduleRequest() const; JSAtom* importName() const; JSAtom* localName() const; uint32_t lineNumber() const; uint32_t columnNumber() const; }; template XDRResult XDRExportEntries(XDRState* xdr, MutableHandleArrayObject vec); using RootedExportEntryObject = Rooted; using HandleExportEntryObject = Handle; class RequestedModuleObject : public NativeObject { public: enum { ModuleSpecifierSlot = 0, LineNumberSlot, ColumnNumberSlot, SlotCount }; static const JSClass class_; static bool isInstance(HandleValue value); static RequestedModuleObject* create(JSContext* cx, HandleAtom moduleSpecifier, uint32_t lineNumber, uint32_t columnNumber); JSAtom* moduleSpecifier() const; uint32_t lineNumber() const; uint32_t columnNumber() const; }; using RootedRequestedModuleObject = Rooted; using HandleRequestedModuleObject = Handle; using RootedRequestedModuleVector = Rooted >; using MutableHandleRequestedModuleObject = MutableHandle; template XDRResult XDRRequestedModuleObject(XDRState* xdr, MutableHandleRequestedModuleObject reqObj); class IndirectBindingMap { public: void trace(JSTracer* trc); bool put(JSContext* cx, HandleId name, HandleModuleEnvironmentObject environment, HandleId localName); 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, Shape** shapeOut) const; template 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, Shape* shape); HeapPtr environment; HeapPtr shape; }; using Map = mozilla::HashMap, ZoneAllocPolicy>; mozilla::Maybe map_; }; class ModuleNamespaceObject : public ProxyObject { public: enum ModuleNamespaceSlot { ExportsSlot = 0, BindingsSlot }; static bool isInstance(HandleValue value); static ModuleNamespaceObject* create(JSContext* cx, HandleModuleObject module, HandleObject exports, UniquePtr bindings); ModuleObject& module(); JSObject& exports(); IndirectBindingMap& bindings(); bool addBinding(JSContext* cx, HandleAtom exportedName, HandleModuleObject targetModule, HandleAtom localName); private: struct ProxyHandler : public BaseProxyHandler { ProxyHandler(); bool getOwnPropertyDescriptor( JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc) const override; bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, Handle 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(JSFreeOp* fop, JSObject* proxy) const override; static const char family; }; bool hasBindings() const; public: static const ProxyHandler proxyHandler; }; using RootedModuleNamespaceObject = Rooted; using HandleModuleNamespaceObject = Handle; // Possible values for ModuleStatus are defined in SelfHostingDefines.h. using ModuleStatus = int32_t; class ModuleObject : public NativeObject { public: enum ModuleSlot { ScriptSlot = 0, EnvironmentSlot, NamespaceSlot, StatusSlot, EvaluationErrorSlot, MetaObjectSlot, ScriptSourceObjectSlot, RequestedModulesSlot, ImportEntriesSlot, LocalExportEntriesSlot, IndirectExportEntriesSlot, StarExportEntriesSlot, ImportBindingsSlot, FunctionDeclarationsSlot, DFSIndexSlot, DFSAncestorIndexSlot, AsyncSlot, AsyncEvaluatingSlot, TopLevelCapabilitySlot, AsyncParentModulesSlot, PendingAsyncDependenciesSlot, SlotCount }; static_assert(EnvironmentSlot == MODULE_OBJECT_ENVIRONMENT_SLOT, "EnvironmentSlot must match self-hosting define"); static_assert(StatusSlot == MODULE_OBJECT_STATUS_SLOT, "StatusSlot must match self-hosting define"); static_assert(EvaluationErrorSlot == MODULE_OBJECT_EVALUATION_ERROR_SLOT, "EvaluationErrorSlot must match self-hosting define"); static_assert(DFSIndexSlot == MODULE_OBJECT_DFS_INDEX_SLOT, "DFSIndexSlot must match self-hosting define"); static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, "DFSAncestorIndexSlot must match self-hosting define"); static_assert(AsyncEvaluatingSlot == MODULE_OBJECT_ASYNC_EVALUATING_SLOT, "AsyncEvaluatingSlot must match self-hosting define"); static_assert(TopLevelCapabilitySlot == MODULE_OBJECT_TOP_LEVEL_CAPABILITY_SLOT, "topLevelCapabilitySlot must match self-hosting define"); static_assert(PendingAsyncDependenciesSlot == MODULE_OBJECT_PENDING_ASYNC_DEPENDENCIES_SLOT, "PendingAsyncDependenciesSlot must match self-hosting define"); static const JSClass class_; static bool isInstance(HandleValue value); static ModuleObject* create(JSContext* cx); // Initialize the slots on this object that are dependent on the script. void initScriptSlots(HandleScript script); void setInitialEnvironment( Handle initialEnvironment); void initStatusSlot(); void initImportExportData(HandleArrayObject requestedModules, HandleArrayObject importEntries, HandleArrayObject localExportEntries, HandleArrayObject indiretExportEntries, HandleArrayObject starExportEntries); static bool Freeze(JSContext* cx, HandleModuleObject self); #ifdef DEBUG static bool AssertFrozen(JSContext* cx, HandleModuleObject self); #endif void fixEnvironmentsAfterRealmMerge(); JSScript* maybeScript() const; JSScript* script() const; Scope* enclosingScope() const; ModuleEnvironmentObject& initialEnvironment() const; ModuleEnvironmentObject* environment() const; ModuleNamespaceObject* namespace_(); ModuleStatus status() const; uint32_t dfsIndex() const; uint32_t dfsAncestorIndex() const; bool hadEvaluationError() const; Value evaluationError() const; JSObject* metaObject() const; ScriptSourceObject* scriptSourceObject() const; ArrayObject& requestedModules() const; ArrayObject& importEntries() const; ArrayObject& localExportEntries() const; ArrayObject& indirectExportEntries() const; ArrayObject& starExportEntries() const; IndirectBindingMap& importBindings(); static PromiseObject* createTopLevelCapability(JSContext* cx, HandleModuleObject module); bool isAsync() const; void setAsync(bool isAsync); bool isAsyncEvaluating() const; void setAsyncEvaluating(bool isEvaluating); void setEvaluationError(HandleValue newValue); void setPendingAsyncDependencies(uint32_t newValue); void setInitialTopLevelCapability(HandleObject promiseObj); bool hasTopLevelCapability() const; JSObject* topLevelCapability() const; ListObject* asyncParentModules() const; uint32_t pendingAsyncDependencies() const; static bool appendAsyncParentModule(JSContext* cx, HandleModuleObject self, HandleModuleObject parent); static bool topLevelCapabilityResolve(JSContext* cx, HandleModuleObject module); static bool topLevelCapabilityReject(JSContext* cx, HandleModuleObject module, HandleValue error); static bool Instantiate(JSContext* cx, HandleModuleObject self); // Start evaluating the module. If TLA is enabled, rval will be a promise static bool Evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval); static ModuleNamespaceObject* GetOrCreateModuleNamespace( JSContext* cx, HandleModuleObject self); void setMetaObject(JSObject* obj); // For intrinsic_InstantiateModuleFunctionDeclarations. static bool instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self); // For intrinsic_ExecuteModule. static bool execute(JSContext* cx, HandleModuleObject self, MutableHandleValue rval); // For intrinsic_NewModuleNamespace. static ModuleNamespaceObject* createNamespace(JSContext* cx, HandleModuleObject self, HandleObject exports); static bool createEnvironment(JSContext* cx, HandleModuleObject self); bool initAsyncSlots(JSContext* cx, bool isAsync, HandleObject asyncParentModulesList); // NOTE: accessor for FunctionDeclarationsSlot is defined inside // ModuleObject.cpp as static function. private: static const JSClassOps classOps_; static void trace(JSTracer* trc, JSObject* obj); static void finalize(JSFreeOp* fop, JSObject* obj); bool hasImportBindings() const; }; JSObject* GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module); JSObject* CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate, HandleString specifier); // https://tc39.es/proposal-top-level-await/#sec-getasynccycleroot ModuleObject* GetAsyncCycleRoot(ModuleObject* module); // https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled void AsyncModuleExecutionFulfilled(JSContext* cx, HandleModuleObject module); // https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected void AsyncModuleExecutionRejected(JSContext* cx, HandleModuleObject module, HandleValue error); // https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionfulfilled bool AsyncModuleExecutionFulfilledHandler(JSContext* cx, unsigned argc, Value* vp); // https://tc39.es/proposal-top-level-await/#sec-asyncmodulexecutionrejected bool AsyncModuleExecutionRejectedHandler(JSContext* cx, unsigned argc, Value* vp); JSObject* StartDynamicModuleImport(JSContext* cx, HandleScript script, HandleValue specifier); bool OnModuleEvaluationFailure(JSContext* cx, HandleObject evaluationPromise); bool FinishDynamicModuleImport(JSContext* cx, HandleObject evaluationPromise, HandleValue referencingPrivate, HandleString specifier, HandleObject promise); // This is used so that Top Level Await functionality can be turned off // entirely. It will be removed in bug#1676612. bool FinishDynamicModuleImport_NoTLA(JSContext* cx, JS::DynamicImportStatus status, HandleValue referencingPrivate, HandleString specifier, HandleObject promise); template XDRResult XDRModuleObject(XDRState* xdr, MutableHandleModuleObject modp); } // namespace js template <> inline bool JSObject::is() const { return js::IsDerivedProxyObject(this, &js::ModuleNamespaceObject::proxyHandler); } #endif /* builtin_ModuleObject_h */