// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_MODULE_H_
#define V8_OBJECTS_MODULE_H_
#include "include/v8-script.h"
#include "include/v8-template.h"
#include "src/objects/js-objects.h"
#include "src/objects/objects.h"
#include "src/objects/struct.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
class JSModuleNamespace;
class SourceTextModuleDescriptor;
class SourceTextModuleInfo;
class SourceTextModuleInfoEntry;
class Zone;
template <typename T>
class ZoneForwardList;
// Module is the base class for ECMAScript module types, roughly corresponding
// to Abstract Module Record.
// https://tc39.es/ecma262/#sec-abstract-module-records
V8_OBJECT class Module : public HeapObject {
V8_IT_ABSTRACT;
public:
DECL_VERIFIER(Module)
DECL_PRINTER(Module)
enum Status {
// Order matters!
kUnlinked,
kPreLinking,
kLinking,
kLinked,
kEvaluating,
kEvaluatingAsync,
kEvaluated,
kErrored
};
#ifdef DEBUG
static const char* StatusString(Module::Status status);
#endif // DEBUG
// The exception in the case {status} is kErrored.
Tagged<Object> GetException();
// Returns if this module or any transitively requested module is [[Async]],
// i.e. has a top-level await.
V8_WARN_UNUSED_RESULT bool IsGraphAsync(Isolate* isolate) const;
struct UserResolveCallbacks {
v8::Module::ResolveModuleCallback module_callback = nullptr;
v8::Module::ResolveSourceCallback source_callback = nullptr;
v8::Module::ResolveModuleByIndexCallback module_callback_by_index = nullptr;
v8::Module::ResolveSourceByIndexCallback source_callback_by_index = nullptr;
};
// Implementation of spec operation ModuleDeclarationInstantiation.
// Returns false if an exception occurred during instantiation, true
// otherwise. (In the case where the callback throws an exception, that
// exception is propagated.)
static V8_WARN_UNUSED_RESULT bool Instantiate(
Isolate* isolate, Handle<Module> module, v8::Local<v8::Context> context,
const UserResolveCallbacks& callbacks);
// Implementation of spec operation ModuleEvaluation.
static V8_WARN_UNUSED_RESULT MaybeDirectHandle<JSPromise> Evaluate(
Isolate* isolate, Handle<Module> module);
// Get the namespace object for [module]. If it doesn't exist yet, it is
// created.
static Handle<Cell> GetModuleNamespaceCell(
Isolate* isolate, Handle<Module> module,
ModuleImportPhase phase = ModuleImportPhase::kEvaluation);
static DirectHandle<JSModuleNamespace> GetModuleNamespace(
Isolate* isolate, Handle<Module> module,
ModuleImportPhase phase = ModuleImportPhase::kEvaluation);
inline Tagged<ObjectHashTable> exports() const;
inline void set_exports(Tagged<ObjectHashTable> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline int hash() const;
inline void set_hash(int value);
inline int status() const;
inline void set_status(int value);
inline Tagged<UnionOf<Cell, Undefined>> module_namespace() const;
inline void set_module_namespace(
Tagged<UnionOf<Cell, Undefined>> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline Tagged<UnionOf<Cell, Undefined>> deferred_module_namespace() const;
inline void set_deferred_module_namespace(
Tagged<UnionOf<Cell, Undefined>> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline Tagged<Object> exception() const;
inline void set_exception(Tagged<Object> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline Tagged<UnionOf<JSPromise, Undefined>> top_level_capability() const;
inline void set_top_level_capability(
Tagged<UnionOf<JSPromise, Undefined>> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
struct Hash;
protected:
friend class Factory;
// The [must_resolve] argument indicates whether or not an exception should be
// thrown in case the module does not provide an export named [name]
// (including when a cycle is detected). An exception is always thrown in the
// case of conflicting star exports.
//
// If [must_resolve] is true, a null result indicates an exception. If
// [must_resolve] is false, a null result may or may not indicate an
// exception (so check manually!).
class ResolveSet;
static V8_WARN_UNUSED_RESULT MaybeHandle<Cell> ResolveExport(
Isolate* isolate, Handle<Module> module,
DirectHandle<String> module_specifier, Handle<String> export_name,
MessageLocation loc, bool must_resolve, ResolveSet* resolve_set);
static V8_WARN_UNUSED_RESULT bool PrepareInstantiate(
Isolate* isolate, DirectHandle<Module> module,
v8::Local<v8::Context> context, const UserResolveCallbacks& callbacks);
static V8_WARN_UNUSED_RESULT bool FinishInstantiate(
Isolate* isolate, Handle<Module> module,
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index,
Zone* zone, unsigned depth, unsigned* max_depth);
// Set module's status back to kUnlinked and reset other internal state.
// This is used when instantiation fails.
static void Reset(Isolate* isolate, DirectHandle<Module> module);
static void ResetGraph(Isolate* isolate, DirectHandle<Module> module);
// To set status to kErrored, RecordError should be used.
void SetStatus(Status status);
void RecordError(Isolate* isolate, Tagged<Object> error);
public:
TaggedMember<ObjectHashTable> exports_;
TaggedMember<Smi> hash_;
TaggedMember<Smi> status_;
TaggedMember<UnionOf<Cell, Undefined>> module_namespace_;
TaggedMember<UnionOf<Cell, Undefined>> deferred_module_namespace_;
TaggedMember<Object> exception_;
TaggedMember<UnionOf<JSPromise, Undefined>> top_level_capability_;
} V8_OBJECT_END;
template <>
struct ObjectTraits<Module> {
using BodyDescriptor = FixedBodyDescriptor<offsetof(Module, exports_),
sizeof(Module), sizeof(Module)>;
};
// When importing a module namespace (import * as foo from "bar"), a
// JSModuleNamespace object (representing module "bar") is created and bound to
// the declared variable (foo). A module can have at most one namespace object.
V8_OBJECT class JSModuleNamespace : public JSSpecialObject {
V8_IT_OWN_TYPE;
public:
inline Tagged<Module> module() const;
inline void set_module(Tagged<Module> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
DECL_PRINTER(JSModuleNamespace)
DECL_VERIFIER(JSModuleNamespace)
// Retrieve the value exported by [module] under the given [name]. If there is
// no such export, return Just(undefined). If the export is uninitialized,
// schedule an exception and return Nothing.
V8_WARN_UNUSED_RESULT MaybeDirectHandle<Object> GetExport(
Isolate* isolate, DirectHandle<String> name);
bool HasExport(Isolate* isolate, DirectHandle<String> name);
// Counts an access to a missing `default` export on the namespace [it] starts
// at, when that module has an `export * from '...'` statement. Such a
// `default` is absent today because `export *` skips it, but would resolve to
// the star-exported default (if it exists) under the export-star-default
// proposal (https://github.com/tc39/proposal-export-star-default).
// Called from [[Get]] and [[HasProperty]]. Only valid while [it] is in the
// LookupIterator::MODULE_NAMESPACE state.
static void MaybeCountMissingDefaultWithStarExport(LookupIterator* it);
// Return the (constant) property attributes for the referenced property,
// which is assumed to correspond to an export. If the export is
// uninitialized, schedule an exception and return Nothing.
static V8_WARN_UNUSED_RESULT Maybe<PropertyAttributes> GetPropertyAttributes(
LookupIterator* it);
static V8_WARN_UNUSED_RESULT Maybe<bool> DefineOwnProperty(
Isolate* isolate, DirectHandle<JSModuleNamespace> o,
DirectHandle<Object> key, PropertyDescriptor* desc,
Maybe<ShouldThrow> should_throw);
// In-object fields. These indices represent both the in-object field index,
// as well as the descriptor index.
enum {
kToStringTagIndex,
kInObjectFieldCount,
};
static const int kHeaderSize;
// We need to include in-object fields
// TODO(v8:8944): improve handling of in-object fields
static const int kSize;
public:
TaggedMember<Module> module_;
} V8_OBJECT_END;
inline constexpr int JSModuleNamespace::kHeaderSize = sizeof(JSModuleNamespace);
inline constexpr int JSModuleNamespace::kSize =
JSModuleNamespace::kHeaderSize + (kTaggedSize * kInObjectFieldCount);
V8_OBJECT class JSDeferredModuleNamespace : public JSModuleNamespace {
public:
DECL_PRINTER(JSDeferredModuleNamespace)
DECL_VERIFIER(JSDeferredModuleNamespace)
static void EvaluateModuleSync(
Isolate* isolate, DirectHandle<JSDeferredModuleNamespace> holder);
static bool TriggersEvaluation(LookupIterator* it);
// We need to include in-object fields
// TODO(v8:8944): improve handling of in-object fields
static const int kSize;
} V8_OBJECT_END;
inline constexpr int JSDeferredModuleNamespace::kSize =
sizeof(JSDeferredModuleNamespace) + (kTaggedSize * kInObjectFieldCount);
V8_OBJECT class ScriptOrModule : public Struct {
public:
inline Tagged<Object> resource_name() const;
inline void set_resource_name(Tagged<Object> value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
inline Tagged<FixedArray> host_defined_options() const;
inline void set_host_defined_options(
Tagged<FixedArray> value, WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
DECL_PRINTER(ScriptOrModule)
DECL_VERIFIER(ScriptOrModule)
using BodyDescriptor = StructBodyDescriptor;
private:
friend class TorqueGeneratedScriptOrModuleAsserts;
TaggedMember<Object> resource_name_;
TaggedMember<FixedArray> host_defined_options_;
} V8_OBJECT_END;
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_MODULE_H_