/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef WABT_INTERPRETER_H_
#define WABT_INTERPRETER_H_

#include <stdint.h>

#include <memory>
#include <vector>

#include "common.h"
#include "binding-hash.h"
#include "opcode.h"
#include "writer.h"

namespace wabt {

class Stream;

#define FOREACH_INTERPRETER_RESULT(V)                                       \
  V(Ok, "ok")                                                               \
  /* returned from the top-most function */                                 \
  V(Returned, "returned")                                                   \
  /* memory access is out of bounds */                                      \
  V(TrapMemoryAccessOutOfBounds, "out of bounds memory access")             \
  /* converting from float -> int would overflow int */                     \
  V(TrapIntegerOverflow, "integer overflow")                                \
  /* dividend is zero in integer divide */                                  \
  V(TrapIntegerDivideByZero, "integer divide by zero")                      \
  /* converting from float -> int where float is nan */                     \
  V(TrapInvalidConversionToInteger, "invalid conversion to integer")        \
  /* function table index is out of bounds */                               \
  V(TrapUndefinedTableIndex, "undefined table index")                       \
  /* function table element is uninitialized */                             \
  V(TrapUninitializedTableElement, "uninitialized table element")           \
  /* unreachable instruction executed */                                    \
  V(TrapUnreachable, "unreachable executed")                                \
  /* call indirect signature doesn't match function table signature */      \
  V(TrapIndirectCallSignatureMismatch, "indirect call signature mismatch")  \
  /* ran out of call stack frames (probably infinite recursion) */          \
  V(TrapCallStackExhausted, "call stack exhausted")                         \
  /* ran out of value stack space */                                        \
  V(TrapValueStackExhausted, "value stack exhausted")                       \
  /* we called a host function, but the return value didn't match the */    \
  /* expected type */                                                       \
  V(TrapHostResultTypeMismatch, "host result type mismatch")                \
  /* we called an import function, but it didn't complete succesfully */    \
  V(TrapHostTrapped, "host function trapped")                               \
  /* we attempted to call a function with the an argument list that doesn't \
   * match the function signature */                                        \
  V(ArgumentTypeMismatch, "argument type mismatch")                         \
  /* we tried to get an export by name that doesn't exist */                \
  V(UnknownExport, "unknown export")                                        \
  /* the expected export kind doesn't match. */                             \
  V(ExportKindMismatch, "export kind mismatch")

enum class InterpreterResult {
#define V(Name, str) Name,
  FOREACH_INTERPRETER_RESULT(V)
#undef V
};

#define WABT_INVALID_INDEX static_cast<uint32_t>(~0)
#define WABT_INVALID_OFFSET static_cast<uint32_t>(~0)
#define WABT_TABLE_ENTRY_SIZE (sizeof(uint32_t) * 2 + sizeof(uint8_t))
#define WABT_TABLE_ENTRY_OFFSET_OFFSET 0
#define WABT_TABLE_ENTRY_DROP_OFFSET sizeof(uint32_t)
#define WABT_TABLE_ENTRY_KEEP_OFFSET (sizeof(uint32_t) * 2)

enum class InterpreterOpcode {
/* push space on the value stack for N entries */
#define WABT_OPCODE(rtype, type1, type2, mem_size, code, Name, text) Name = code,
#include "interpreter-opcode.def"
#undef WABT_OPCODE

      First = static_cast<int>(Opcode::First),
  Last = DropKeep,
};
static const int kInterpreterOpcodeCount = WABT_ENUM_COUNT(InterpreterOpcode);

struct InterpreterFuncSignature {
  std::vector<Type> param_types;
  std::vector<Type> result_types;
};

struct InterpreterTable {
  explicit InterpreterTable(const Limits& limits)
      : limits(limits), func_indexes(limits.initial, WABT_INVALID_INDEX) {}

  Limits limits;
  std::vector<uint32_t> func_indexes;
};

struct InterpreterMemory {
  InterpreterMemory() {
    WABT_ZERO_MEMORY(page_limits);
  }
  explicit InterpreterMemory(const Limits& limits)
      : page_limits(limits), data(limits.initial * WABT_PAGE_SIZE) {}

  Limits page_limits;
  std::vector<char> data;
};

union InterpreterValue {
  uint32_t i32;
  uint64_t i64;
  uint32_t f32_bits;
  uint64_t f64_bits;
};

struct InterpreterTypedValue {
  InterpreterTypedValue() {}
  explicit InterpreterTypedValue(Type type): type(type) {}
  InterpreterTypedValue(Type type, const InterpreterValue& value)
      : type(type), value(value) {}

  Type type;
  InterpreterValue value;
};

struct InterpreterGlobal {
  InterpreterGlobal() : mutable_(false), import_index(WABT_INVALID_INDEX) {}
  InterpreterGlobal(const InterpreterTypedValue& typed_value, bool mutable_)
      : typed_value(typed_value), mutable_(mutable_) {}

  InterpreterTypedValue typed_value;
  bool mutable_;
  uint32_t import_index; /* or INVALID_INDEX if not imported */
};

struct InterpreterImport {
  InterpreterImport();
  InterpreterImport(InterpreterImport&&);
  InterpreterImport& operator=(InterpreterImport&&);
  ~InterpreterImport();

  StringSlice module_name;
  StringSlice field_name;
  ExternalKind kind;
  union {
    struct {
      uint32_t sig_index;
    } func;
    struct {
      Limits limits;
    } table, memory;
    struct {
      Type type;
      bool mutable_;
    } global;
  };
};

struct InterpreterFunc;

typedef Result (*InterpreterHostFuncCallback)(
    const struct HostInterpreterFunc* func,
    const InterpreterFuncSignature* sig,
    uint32_t num_args,
    InterpreterTypedValue* args,
    uint32_t num_results,
    InterpreterTypedValue* out_results,
    void* user_data);

struct InterpreterFunc {
  WABT_DISALLOW_COPY_AND_ASSIGN(InterpreterFunc);
  InterpreterFunc(uint32_t sig_index, bool is_host)
      : sig_index(sig_index), is_host(is_host) {}
  virtual ~InterpreterFunc() {}

  inline struct DefinedInterpreterFunc* as_defined();
  inline struct HostInterpreterFunc* as_host();

  uint32_t sig_index;
  bool is_host;
};

struct DefinedInterpreterFunc : InterpreterFunc {
  DefinedInterpreterFunc(uint32_t sig_index)
      : InterpreterFunc(sig_index, false),
        offset(WABT_INVALID_INDEX),
        local_decl_count(0),
        local_count(0) {}

  uint32_t offset;
  uint32_t local_decl_count;
  uint32_t local_count;
  std::vector<Type> param_and_local_types;
};

struct HostInterpreterFunc : InterpreterFunc {
  HostInterpreterFunc(const StringSlice& module_name,
                      const StringSlice& field_name,
                      uint32_t sig_index)
      : InterpreterFunc(sig_index, true),
        module_name(module_name),
        field_name(field_name) {}

  StringSlice module_name;
  StringSlice field_name;
  InterpreterHostFuncCallback callback;
  void* user_data;
};

DefinedInterpreterFunc* InterpreterFunc::as_defined() {
  assert(!is_host);
  return static_cast<DefinedInterpreterFunc*>(this);
}

HostInterpreterFunc* InterpreterFunc::as_host() {
  assert(is_host);
  return static_cast<HostInterpreterFunc*>(this);
}

struct InterpreterExport {
  InterpreterExport(const StringSlice& name, ExternalKind kind, uint32_t index)
      : name(name), kind(kind), index(index) {}
  InterpreterExport(InterpreterExport&&);
  InterpreterExport& operator=(InterpreterExport&&);
  ~InterpreterExport();

  StringSlice name;
  ExternalKind kind;
  uint32_t index;
};

struct PrintErrorCallback {
  void* user_data;
  void (*print_error)(const char* msg, void* user_data);
};

struct InterpreterHostImportDelegate {
  void* user_data;
  Result (*import_func)(InterpreterImport*,
                        InterpreterFunc*,
                        InterpreterFuncSignature*,
                        PrintErrorCallback,
                        void* user_data);
  Result (*import_table)(InterpreterImport*,
                         InterpreterTable*,
                         PrintErrorCallback,
                         void* user_data);
  Result (*import_memory)(InterpreterImport*,
                          InterpreterMemory*,
                          PrintErrorCallback,
                          void* user_data);
  Result (*import_global)(InterpreterImport*,
                          InterpreterGlobal*,
                          PrintErrorCallback,
                          void* user_data);
};

struct InterpreterModule {
  WABT_DISALLOW_COPY_AND_ASSIGN(InterpreterModule);
  explicit InterpreterModule(bool is_host);
  InterpreterModule(const StringSlice& name, bool is_host);
  virtual ~InterpreterModule();

  inline struct DefinedInterpreterModule* as_defined();
  inline struct HostInterpreterModule* as_host();

  StringSlice name;
  std::vector<InterpreterExport> exports;
  BindingHash export_bindings;
  uint32_t memory_index; /* INVALID_INDEX if not defined */
  uint32_t table_index;  /* INVALID_INDEX if not defined */
  bool is_host;
};

struct DefinedInterpreterModule : InterpreterModule {
  explicit DefinedInterpreterModule(size_t istream_start);

  std::vector<InterpreterImport> imports;
  uint32_t start_func_index; /* INVALID_INDEX if not defined */
  size_t istream_start;
  size_t istream_end;
};

struct HostInterpreterModule : InterpreterModule {
  HostInterpreterModule(const StringSlice& name);

  InterpreterHostImportDelegate import_delegate;
};

DefinedInterpreterModule* InterpreterModule::as_defined() {
  assert(!is_host);
  return static_cast<DefinedInterpreterModule*>(this);
}

HostInterpreterModule* InterpreterModule::as_host() {
  assert(is_host);
  return static_cast<HostInterpreterModule*>(this);
}

/* Used to track and reset the state of the environment. */
struct InterpreterEnvironmentMark {
  size_t modules_size;
  size_t sigs_size;
  size_t funcs_size;
  size_t memories_size;
  size_t tables_size;
  size_t globals_size;
  size_t istream_size;
};

struct InterpreterEnvironment {
  InterpreterEnvironment();

  std::vector<std::unique_ptr<InterpreterModule>> modules;
  std::vector<InterpreterFuncSignature> sigs;
  std::vector<std::unique_ptr<InterpreterFunc>> funcs;
  std::vector<InterpreterMemory> memories;
  std::vector<InterpreterTable> tables;
  std::vector<InterpreterGlobal> globals;
  std::unique_ptr<OutputBuffer> istream;
  BindingHash module_bindings;
  BindingHash registered_module_bindings;
};

struct InterpreterThread {
  InterpreterThread();

  InterpreterEnvironment* env;
  std::vector<InterpreterValue> value_stack;
  std::vector<uint32_t> call_stack;
  InterpreterValue* value_stack_top;
  InterpreterValue* value_stack_end;
  uint32_t* call_stack_top;
  uint32_t* call_stack_end;
  uint32_t pc;
};

#define WABT_INTERPRETER_THREAD_OPTIONS_DEFAULT \
  { 512 * 1024 / sizeof(InterpreterValue), 64 * 1024, WABT_INVALID_OFFSET }

struct InterpreterThreadOptions {
  uint32_t value_stack_size;
  uint32_t call_stack_size;
  uint32_t pc;
};

bool is_canonical_nan_f32(uint32_t f32_bits);
bool is_canonical_nan_f64(uint64_t f64_bits);
bool is_arithmetic_nan_f32(uint32_t f32_bits);
bool is_arithmetic_nan_f64(uint64_t f64_bits);
bool func_signatures_are_equal(InterpreterEnvironment* env,
                               uint32_t sig_index_0,
                               uint32_t sig_index_1);

void destroy_interpreter_environment(InterpreterEnvironment* env);
InterpreterEnvironmentMark mark_interpreter_environment(
    InterpreterEnvironment* env);
void reset_interpreter_environment_to_mark(InterpreterEnvironment* env,
                                           InterpreterEnvironmentMark mark);
HostInterpreterModule* append_host_module(InterpreterEnvironment* env,
                                          StringSlice name);
void init_interpreter_thread(InterpreterEnvironment* env,
                             InterpreterThread* thread,
                             InterpreterThreadOptions* options);
InterpreterResult push_thread_value(InterpreterThread* thread,
                                    InterpreterValue value);
void destroy_interpreter_thread(InterpreterThread* thread);
InterpreterResult call_host(InterpreterThread* thread,
                            HostInterpreterFunc* func);
InterpreterResult run_interpreter(InterpreterThread* thread,
                                  uint32_t num_instructions,
                                  uint32_t* call_stack_return_top);
void trace_pc(InterpreterThread* thread, Stream* stream);
void disassemble(InterpreterEnvironment* env,
                 Stream* stream,
                 uint32_t from,
                 uint32_t to);
void disassemble_module(InterpreterEnvironment* env,
                        Stream* stream,
                        InterpreterModule* module);

InterpreterExport* get_interpreter_export_by_name(InterpreterModule* module,
                                                  const StringSlice* name);

}  // namespace wabt

#endif /* WABT_INTERPRETER_H_ */
