text: // Copyright 2015 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.
  
  #if !V8_ENABLE_WEBASSEMBLY
  #error This header should only be included if WebAssembly is enabled.
  #endif  // !V8_ENABLE_WEBASSEMBLY
  
  #ifndef V8_WASM_WASM_MODULE_H_
  #define V8_WASM_WASM_MODULE_H_
  
  #include <memory>
  
  #include "src/base/optional.h"
  #include "src/base/platform/wrappers.h"
  #include "src/common/globals.h"
  #include "src/handles/handles.h"
  #include "src/utils/vector.h"
  #include "src/wasm/branch-hint-map.h"
  #include "src/wasm/signature-map.h"
  #include "src/wasm/struct-types.h"
  #include "src/wasm/wasm-constants.h"
  #include "src/wasm/wasm-init-expr.h"
  
  namespace v8 {
  
  namespace internal {
  
  class WasmModuleObject;
  
  namespace wasm {
  
  using WasmName = Vector<const char>;
  
  struct AsmJsOffsets;
  class ErrorThrower;
  
  // Reference to a string in the wire bytes.
  class WireBytesRef {
   public:
    WireBytesRef() : WireBytesRef(0, 0) {}
    WireBytesRef(uint32_t offset, uint32_t length)
        : offset_(offset), length_(length) {
      DCHECK_IMPLIES(offset_ == 0, length_ == 0);
      DCHECK_LE(offset_, offset_ + length_);  // no uint32_t overflow.
    }
  
    uint32_t offset() const { return offset_; }
    uint32_t length() const { return length_; }
    uint32_t end_offset() const { return offset_ + length_; }
    bool is_empty() const { return length_ == 0; }
    bool is_set() const { return offset_ != 0; }
  
   private:
    uint32_t offset_;
    uint32_t length_;
  };
  
  // Static representation of a wasm function.
  struct WasmFunction {
    const FunctionSig* sig;  // signature of the function.
    uint32_t func_index;     // index into the function table.
    uint32_t sig_index;      // index into the signature table.
    WireBytesRef code;       // code of this function.
    bool imported;
    bool exported;
    bool declared;
  };
  
  // Static representation of a wasm global variable.
  struct WasmGlobal {
    ValueType type;     // type of the global.
    bool mutability;    // {true} if mutable.
    WasmInitExpr init;  // the initialization expression of the global.
    union {
      uint32_t index;   // index of imported mutable global.
      uint32_t offset;  // offset into global memory (if not imported & mutable).
    };
    bool imported;  // true if imported.
    bool exported;  // true if exported.
  };
  
  // Note: An exception signature only uses the params portion of a
  // function signature.
  using WasmExceptionSig = FunctionSig;
  
  // Static representation of a wasm exception type.
  struct WasmException {
    explicit WasmException(const WasmExceptionSig* sig) : sig(sig) {}
    const FunctionSig* ToFunctionSig() const { return sig; }
  
    const WasmExceptionSig* sig;  // type signature of the exception.
  };
  
  // Static representation of a wasm data segment.
  struct WasmDataSegment {
    // Construct an active segment.
    explicit WasmDataSegment(WasmInitExpr dest_addr)
        : dest_addr(std::move(dest_addr)), active(true) {}
  
    // Construct a passive segment, which has no dest_addr.
    WasmDataSegment() : active(false) {}
  
    WasmInitExpr dest_addr;  // destination memory address of the data.
    WireBytesRef source;     // start offset in the module bytes.
    bool active = true;      // true if copied automatically during instantiation.
  };
  
  // Static representation of wasm element segment (table initializer).
  struct WasmElemSegment {
    // Construct an active segment.
    WasmElemSegment(ValueType type, uint32_t table_index, WasmInitExpr offset)
        : type(type),
          table_index(table_index),
          offset(std::move(offset)),
          status(kStatusActive) {}
  
    // Construct a passive or declarative segment, which has no table index or
    // offset.
    WasmElemSegment(ValueType type, bool declarative)
        : type(type),
          table_index(0),
          status(declarative ? kStatusDeclarative : kStatusPassive) {}
  
    // Construct a passive or declarative segment, which has no table index or
    // offset.
    WasmElemSegment()
        : type(kWasmBottom), table_index(0), status(kStatusActive) {}
  
    WasmElemSegment(const WasmElemSegment&) = delete;
    WasmElemSegment(WasmElemSegment&&) V8_NOEXCEPT = default;
    WasmElemSegment& operator=(const WasmElemSegment&) = delete;
    WasmElemSegment& operator=(WasmElemSegment&&) V8_NOEXCEPT = default;
  
    ValueType type;
    uint32_t table_index;
    WasmInitExpr offset;
    std::vector<WasmInitExpr> entries;
    enum Status {
      kStatusActive,      // copied automatically during instantiation.
      kStatusPassive,     // copied explicitly after instantiation.
      kStatusDeclarative  // purely declarative and never copied.
    } status;
  };
  
  // Static representation of a wasm import.
  struct WasmImport {
    WireBytesRef module_name;   // module name.
    WireBytesRef field_name;    // import name.
    ImportExportKindCode kind;  // kind of the import.
    uint32_t index;             // index into the respective space.
  };
  
  // Static representation of a wasm export.
  struct WasmExport {
    WireBytesRef name;          // exported name.
    ImportExportKindCode kind;  // kind of the export.
    uint32_t index;             // index into the respective space.
  };
  
  enum class WasmCompilationHintStrategy : uint8_t {
    kDefault = 0,
    kLazy = 1,
    kEager = 2,
    kLazyBaselineEagerTopTier = 3,
  };
  
  enum class WasmCompilationHintTier : uint8_t {
    kDefault = 0,
    kBaseline = 1,
    kOptimized = 2,
  };
  
  // Static representation of a wasm compilation hint
  struct WasmCompilationHint {
    WasmCompilationHintStrategy strategy;
    WasmCompilationHintTier baseline_tier;
    WasmCompilationHintTier top_tier;
  };
  
  enum ModuleOrigin : uint8_t {
    kWasmOrigin,
    kAsmJsSloppyOrigin,
    kAsmJsStrictOrigin
  };
  
  #define SELECT_WASM_COUNTER(counters, origin, prefix, suffix)     \
    ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \
                             : (counters)->prefix##_asm_##suffix())
  
  struct ModuleWireBytes;
  
  class V8_EXPORT_PRIVATE LazilyGeneratedNames {
   public:
    WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes,
                                    uint32_t function_index) const;
  
    void AddForTesting(int function_index, WireBytesRef name);
  
   private:
    // {function_names_} are populated lazily after decoding, and
    // therefore need a mutex to protect concurrent modifications
    // from multiple {WasmModuleObject}.
    mutable base::Mutex mutex_;
    mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>>
        function_names_;
  };
  
  class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
   public:
    explicit AsmJsOffsetInformation(Vector<const byte> encoded_offsets);
  
    // Destructor defined in wasm-module.cc, where the definition of
    // {AsmJsOffsets} is available.
    ~AsmJsOffsetInformation();
  
    int GetSourcePosition(int func_index, int byte_offset,
                          bool is_at_number_conversion);
  
    std::pair<int, int> GetFunctionOffsets(int func_index);
  
   private:
    void EnsureDecodedOffsets();
  
    // The offset information table is decoded lazily, hence needs to be
    // protected against concurrent accesses.
    // Exactly one of the two fields below will be set at a time.
    mutable base::Mutex mutex_;
  
    // Holds the encoded offset table bytes.
    OwnedVector<const uint8_t> encoded_offsets_;
  
    // Holds the decoded offset table.
    std::unique_ptr<AsmJsOffsets> decoded_offsets_;
  };
  
  struct TypeDefinition {
    explicit TypeDefinition(const FunctionSig* sig) : function_sig(sig) {}
    explicit TypeDefinition(const StructType* type) : struct_type(type) {}
    explicit TypeDefinition(const ArrayType* type) : array_type(type) {}
    union {
      const FunctionSig* function_sig;
      const StructType* struct_type;
      const ArrayType* array_type;
    };
  };
  
  struct V8_EXPORT_PRIVATE WasmDebugSymbols {
    enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF };
    Type type = Type::None;
    WireBytesRef external_url;
  };
  
  struct WasmTable;
  
  // Static representation of a module.
  struct V8_EXPORT_PRIVATE WasmModule {
    std::unique_ptr<Zone> signature_zone;
    uint32_t initial_pages = 0;      // initial size of the memory in 64k pages
    uint32_t maximum_pages = 0;      // maximum size of the memory in 64k pages
    bool has_shared_memory = false;  // true if memory is a SharedArrayBuffer
    bool has_maximum_pages = false;  // true if there is a maximum memory size
    bool is_memory64 = false;        // true if the memory is 64 bit
    bool has_memory = false;         // true if the memory was defined or imported
    bool mem_export = false;         // true if the memory is exported
    int start_function_index = -1;   // start function, >= 0 if any
  
    std::vector<WasmGlobal> globals;
    // Size of the buffer required for all globals that are not imported and
    // mutable.
    uint32_t untagged_globals_buffer_size = 0;
    uint32_t tagged_globals_buffer_size = 0;
    uint32_t num_imported_mutable_globals = 0;
    uint32_t num_imported_functions = 0;
    uint32_t num_imported_tables = 0;
    uint32_t num_declared_functions = 0;  // excluding imported
    uint32_t num_exported_functions = 0;
    uint32_t num_declared_data_segments = 0;  // From the DataCount section.
    // Position and size of the code section (payload only, i.e. without section
    // ID and length).
    WireBytesRef code = {0, 0};
    WireBytesRef name = {0, 0};
    std::vector<TypeDefinition> types;  // by type index
    std::vector<uint8_t> type_kinds;    // by type index
    // Map from each type index to the index of its corresponding canonical type.
    // Note: right now, only functions are canonicalized, and arrays and structs
    // map to themselves.
    std::vector<uint32_t> canonicalized_type_ids;
  
    bool has_type(uint32_t index) const { return index < types.size(); }
  
    void add_signature(const FunctionSig* sig) {
      types.push_back(TypeDefinition(sig));
      type_kinds.push_back(kWasmFunctionTypeCode);
      uint32_t canonical_id = sig ? signature_map.FindOrInsert(*sig) : 0;
      canonicalized_type_ids.push_back(canonical_id);
    }
    bool has_signature(uint32_t index) const {
      return index < types.size() && type_kinds[index] == kWasmFunctionTypeCode;
    }
    const FunctionSig* signature(uint32_t index) const {
      DCHECK(has_signature(index));
      return types[index].function_sig;
    }
  
    void add_struct_type(const StructType* type) {
      types.push_back(TypeDefinition(type));
      type_kinds.push_back(kWasmStructTypeCode);
      // No canonicalization for structs.
      canonicalized_type_ids.push_back(0);
    }
    bool has_struct(uint32_t index) const {
      return index < types.size() && type_kinds[index] == kWasmStructTypeCode;
    }
    const StructType* struct_type(uint32_t index) const {
      DCHECK(has_struct(index));
      return types[index].struct_type;
    }
  
    void add_array_type(const ArrayType* type) {
      types.push_back(TypeDefinition(type));
      type_kinds.push_back(kWasmArrayTypeCode);
      // No canonicalization for arrays.
      canonicalized_type_ids.push_back(0);
    }
    bool has_array(uint32_t index) const {
      return index < types.size() && type_kinds[index] == kWasmArrayTypeCode;
    }
    const ArrayType* array_type(uint32_t index) const {
      DCHECK(has_array(index));
      return types[index].array_type;
    }
  
    std::vector<WasmFunction> functions;
    std::vector<WasmDataSegment> data_segments;
    std::vector<WasmTable> tables;
    std::vector<WasmImport> import_table;
    std::vector<WasmExport> export_table;
    std::vector<WasmException> exceptions;
    std::vector<WasmElemSegment> elem_segments;
    std::vector<WasmCompilationHint> compilation_hints;
    BranchHintInfo branch_hints;
    SignatureMap signature_map;  // canonicalizing map for signature indexes.
  
    ModuleOrigin origin = kWasmOrigin;  // origin of the module
    LazilyGeneratedNames lazily_generated_names;
    WasmDebugSymbols debug_symbols;
  
    // Asm.js source position information. Only available for modules compiled
    // from asm.js.
    std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information;
  
    explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr);
    WasmModule(const WasmModule&) = delete;
    ~WasmModule();
    WasmModule& operator=(const WasmModule&) = delete;
  };
  
  // Static representation of a wasm indirect call table.
  struct WasmTable {
    MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable);
  
    // 'module' can be nullptr
    // TODO(9495): Update this function as more table types are supported, or
    // remove it completely when all reference types are allowed.
    static bool IsValidTableType(ValueType type, const WasmModule* module) {
      if (!type.is_object_reference()) return false;
      HeapType heap_type = type.heap_type();
      return heap_type == HeapType::kFunc || heap_type == HeapType::kExtern ||
             (module != nullptr && heap_type.is_index() &&
              module->has_signature(heap_type.ref_index()));
    }
  
    ValueType type = kWasmVoid;     // table type.
    uint32_t initial_size = 0;      // initial table size.
    uint32_t maximum_size = 0;      // maximum table size.
    bool has_maximum_size = false;  // true if there is a maximum size.
    bool imported = false;          // true if imported.
    bool exported = false;          // true if exported.
    WasmInitExpr initial_value;
  };
  
  inline bool is_asmjs_module(const WasmModule* module) {
    return module->origin != kWasmOrigin;
  }
  
  size_t EstimateStoredSize(const WasmModule* module);
  
  // Returns the number of possible export wrappers for a given module.
  V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module);
  
  // Returns the wrapper index for a function in {module} with signature {sig}
  // and origin defined by {is_import}.
  int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
                            bool is_import);
  
  // Return the byte offset of the function identified by the given index.
  // The offset will be relative to the start of the module bytes.
  // Returns -1 if the function index is invalid.
  int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index);
  
  // Returns the function containing the given byte offset.
  // Returns -1 if the byte offset is not contained in any
  // function of this module.
  int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset);
  
  // Returns the function containing the given byte offset.
  // Will return preceding function if the byte offset is not
  // contained within a function.
  int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
  
  // Interface to the storage (wire bytes) of a wasm module.
  // It is illegal for anyone receiving a ModuleWireBytes to store pointers based
  // on module_bytes, as this storage is only guaranteed to be alive as long as
  // this struct is alive.
  struct V8_EXPORT_PRIVATE ModuleWireBytes {
    explicit ModuleWireBytes(Vector<const byte> module_bytes)
        : module_bytes_(module_bytes) {}
    ModuleWireBytes(const byte* start, const byte* end)
        : module_bytes_(start, static_cast<int>(end - start)) {
      DCHECK_GE(kMaxInt, end - start);
    }
  
    // Get a string stored in the module bytes representing a name.
    WasmName GetNameOrNull(WireBytesRef ref) const;
  
    // Get a string stored in the module bytes representing a function name.
    WasmName GetNameOrNull(const WasmFunction* function,
                           const WasmModule* module) const;
  
    // Checks the given reference is contained within the module bytes.
    bool BoundsCheck(WireBytesRef ref) const {
      uint32_t size = static_cast<uint32_t>(module_bytes_.length());
      return ref.offset() <= size && ref.length() <= size - ref.offset();
    }
  
    Vector<const byte> GetFunctionBytes(const WasmFunction* function) const {
      return module_bytes_.SubVector(function->code.offset(),
                                     function->code.end_offset());
    }
  
    Vector<const byte> module_bytes() const { return module_bytes_; }
    const byte* start() const { return module_bytes_.begin(); }
    const byte* end() const { return module_bytes_.end(); }
    size_t length() const { return module_bytes_.length(); }
  
   private:
    Vector<const byte> module_bytes_;
  };
  
  // A helper for printing out the names of functions.
  struct WasmFunctionName {
    WasmFunctionName(const WasmFunction* function, WasmName name)
        : function_(function), name_(name) {}
  
    const WasmFunction* function_;
    const WasmName name_;
  };
  
  std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
  
  V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
                                              Handle<Context> context);
  
  Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig);
  Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
                                    ValueType type);
  Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size,
                                    base::Optional<uint32_t> max_size);
  Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type,
                                   uint32_t min_size,
                                   base::Optional<uint32_t> max_size);
  Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module);
  Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module);
  Handle<JSArray> GetCustomSections(Isolate* isolate,
                                    Handle<WasmModuleObject> module,
                                    Handle<String> name, ErrorThrower* thrower);
  
  // Get the source position from a given function index and byte offset,
  // for either asm.js or pure Wasm modules.
  int GetSourcePosition(const WasmModule*, uint32_t func_index,
                        uint32_t byte_offset, bool is_at_number_conversion);
  
  // Translate function index to the index relative to the first declared (i.e.
  // non-imported) function.
  inline int declared_function_index(const WasmModule* module, int func_index) {
    DCHECK_LE(module->num_imported_functions, func_index);
    int declared_idx = func_index - module->num_imported_functions;
    DCHECK_GT(module->num_declared_functions, declared_idx);
    return declared_idx;
  }
  
  // TruncatedUserString makes it easy to output names up to a certain length, and
  // output a truncation followed by '...' if they exceed a limit.
  // Use like this:
  //   TruncatedUserString<> name (pc, len);
  //   printf("... %.*s ...", name.length(), name.start())
  template <int kMaxLen = 50>
  class TruncatedUserString {
    static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)");
  
   public:
    template <typename T>
    explicit TruncatedUserString(Vector<T> name)
        : TruncatedUserString(name.begin(), name.length()) {}
  
    TruncatedUserString(const byte* start, size_t len)
        : TruncatedUserString(reinterpret_cast<const char*>(start), len) {}
  
    TruncatedUserString(const char* start, size_t len)
        : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) {
      if (len > static_cast<size_t>(kMaxLen)) {
        base::Memcpy(buffer_, start, kMaxLen - 3);
        memset(buffer_ + kMaxLen - 3, '.', 3);
        start_ = buffer_;
      }
    }
  
    const char* start() const { return start_; }
  
    int length() const { return length_; }
  
   private:
    const char* start_;
    const int length_;
    char buffer_[kMaxLen];
  };
  
  // Print the signature into the given {buffer}, using {delimiter} as separator
  // between parameter types and return types. If {buffer} is non-empty, it will
  // be null-terminated, even if the signature is cut off. Returns the number of
  // characters written, excluding the terminating null-byte.
  size_t PrintSignature(Vector<char> buffer, const wasm::FunctionSig*,
                        char delimiter = ':');
  
  }  // namespace wasm
  }  // namespace internal
  }  // namespace v8
  
  #endif  // V8_WASM_WASM_MODULE_H_
  