// 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_MODULE_DECODER_H_
#define V8_WASM_MODULE_DECODER_H_

#include <memory>

#include "src/common/globals.h"
#include "src/logging/metrics.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-result.h"

namespace v8 {
namespace internal {

class Counters;

namespace wasm {

struct CompilationEnv;

inline bool IsValidSectionCode(uint8_t byte) {
  // Allow everything within [kUnknownSectionCode, kLastKnownModuleSection].
  static_assert(kUnknownSectionCode == 0);
  return byte <= kLastKnownModuleSection;
}

V8_EXPORT_PRIVATE const char* SectionName(SectionCode code);

using ModuleResult = Result<std::shared_ptr<WasmModule>>;
using FunctionResult = Result<std::unique_ptr<WasmFunction>>;
using FunctionOffsets = std::vector<std::pair<int, int>>;
using FunctionOffsetsResult = Result<FunctionOffsets>;

struct AsmJsOffsetEntry {
  int byte_offset;
  int source_position_call;
  int source_position_number_conversion;
};
struct AsmJsOffsetFunctionEntries {
  int start_offset;
  int end_offset;
  std::vector<AsmJsOffsetEntry> entries;
};
struct AsmJsOffsets {
  std::vector<AsmJsOffsetFunctionEntries> functions;
};
using AsmJsOffsetsResult = Result<AsmJsOffsets>;

class DecodedNameSection {
 public:
  explicit DecodedNameSection(base::Vector<const uint8_t> wire_bytes,
                              WireBytesRef name_section);

 private:
  friend class NamesProvider;

  IndirectNameMap local_names_;
  IndirectNameMap label_names_;
  NameMap type_names_;
  NameMap table_names_;
  NameMap memory_names_;
  NameMap global_names_;
  NameMap element_segment_names_;
  NameMap data_segment_names_;
  IndirectNameMap field_names_;
  NameMap tag_names_;
};

enum class DecodingMethod {
  kSync,
  kAsync,
  kSyncStream,
  kAsyncStream,
  kDeserialize
};

// Decodes the bytes of a wasm module between {module_start} and {module_end}.
V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(
    const WasmFeatures& enabled, const byte* module_start,
    const byte* module_end, bool verify_functions, ModuleOrigin origin,
    Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder,
    v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method,
    AccountingAllocator* allocator);
// Stripped down version for disassembler needs.
V8_EXPORT_PRIVATE ModuleResult DecodeWasmModuleForDisassembler(
    const byte* module_start, const byte* module_end,
    AccountingAllocator* allocator);

// Exposed for testing. Decodes a single function signature, allocating it
// in the given zone. Returns {nullptr} upon failure.
V8_EXPORT_PRIVATE const FunctionSig* DecodeWasmSignatureForTesting(
    const WasmFeatures& enabled, Zone* zone, const byte* start,
    const byte* end);

// Decodes the bytes of a wasm function between
// {function_start} and {function_end}.
V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunctionForTesting(
    const WasmFeatures& enabled, Zone* zone, const ModuleWireBytes& wire_bytes,
    const WasmModule* module, const byte* function_start,
    const byte* function_end, Counters* counters);

V8_EXPORT_PRIVATE ConstantExpression
DecodeWasmInitExprForTesting(const WasmFeatures& enabled, const byte* start,
                             const byte* end, ValueType expected);

struct CustomSectionOffset {
  WireBytesRef section;
  WireBytesRef name;
  WireBytesRef payload;
};

V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections(
    const byte* start, const byte* end);

// Extracts the mapping from wasm byte offset to asm.js source position per
// function.
AsmJsOffsetsResult DecodeAsmJsOffsets(
    base::Vector<const uint8_t> encoded_offsets);

// Decode the function names from the name section. Returns the result as an
// unordered map. Only names with valid utf8 encoding are stored and conflicts
// are resolved by choosing the last name read.
void DecodeFunctionNames(const byte* module_start, const byte* module_end,
                         NameMap& names);

class ModuleDecoderImpl;

class ModuleDecoder {
 public:
  explicit ModuleDecoder(const WasmFeatures& enabled);
  ~ModuleDecoder();

  void StartDecoding(Counters* counters,
                     std::shared_ptr<metrics::Recorder> metrics_recorder,
                     v8::metrics::Recorder::ContextId context_id,
                     AccountingAllocator* allocator,
                     ModuleOrigin origin = ModuleOrigin::kWasmOrigin);

  void DecodeModuleHeader(base::Vector<const uint8_t> bytes, uint32_t offset);

  void DecodeSection(SectionCode section_code,
                     base::Vector<const uint8_t> bytes, uint32_t offset,
                     bool verify_functions = true);

  void StartCodeSection(WireBytesRef section_bytes);

  bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset);

  void DecodeFunctionBody(uint32_t index, uint32_t size, uint32_t offset,
                          bool verify_functions = true);

  ModuleResult FinishDecoding(bool verify_functions = true);

  const std::shared_ptr<WasmModule>& shared_module() const;

  WasmModule* module() const { return shared_module().get(); }

  bool ok();

  // Translates the unknown section that decoder is pointing to to an extended
  // SectionCode if the unknown section is known to decoder.
  // The decoder is expected to point after the section length and just before
  // the identifier string of the unknown section.
  // The return value is the number of bytes that were consumed.
  static size_t IdentifyUnknownSection(ModuleDecoder* decoder,
                                       base::Vector<const uint8_t> bytes,
                                       uint32_t offset, SectionCode* result);

 private:
  const WasmFeatures enabled_features_;
  std::unique_ptr<ModuleDecoderImpl> impl_;
};

}  // namespace wasm
}  // namespace internal
}  // namespace v8

#endif  // V8_WASM_MODULE_DECODER_H_