// 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.

#ifndef V8_WASM_RESULT_H_
#define V8_WASM_RESULT_H_

#include <cstdarg>
#include <memory>

#include "src/base/compiler-specific.h"
#include "src/utils.h"

#include "src/handles.h"
#include "src/globals.h"

namespace v8 {
namespace internal {

class Isolate;

namespace wasm {

// Base class for Result<T>.
class V8_EXPORT_PRIVATE ResultBase {
 protected:
  ResultBase(ResultBase&& other)
      : error_offset_(other.error_offset_),
        error_msg_(std::move(other.error_msg_)) {}
  ResultBase() = default;

  ResultBase& operator=(ResultBase&& other) = default;

 public:
  void error(uint32_t offset, std::string error_msg);

  void PRINTF_FORMAT(2, 3) error(const char* format, ...) {
    va_list args;
    va_start(args, format);
    verror(format, args);
    va_end(args);
  }

  void PRINTF_FORMAT(2, 0) verror(const char* format, va_list args);

  void MoveErrorFrom(ResultBase& that) {
    error_offset_ = that.error_offset_;
    // Use {swap()} + {clear()} instead of move assign, as {that} might still
    // be used afterwards.
    error_msg_.swap(that.error_msg_);
    that.error_msg_.clear();
  }

  bool ok() const { return error_msg_.empty(); }
  bool failed() const { return !ok(); }

  uint32_t error_offset() const { return error_offset_; }
  const std::string& error_msg() const { return error_msg_; }

 private:
  uint32_t error_offset_ = 0;
  std::string error_msg_;
};

// The overall result of decoding a function or a module.
template <typename T>
class Result : public ResultBase {
 public:
  Result() = default;

  template <typename S>
  explicit Result(S&& value) : val(std::forward<S>(value)) {}

  template <typename S>
  Result(Result<S>&& other)
      : ResultBase(std::move(other)), val(std::move(other.val)) {}

  Result& operator=(Result&& other) = default;

  static Result<T> PRINTF_FORMAT(1, 2) Error(const char* format, ...) {
    va_list args;
    va_start(args, format);
    Result<T> result;
    result.verror(format, args);
    va_end(args);
    return result;
  }

  T val = T{};

 private:
  DISALLOW_COPY_AND_ASSIGN(Result);
};

// A helper for generating error messages that bubble up to JS exceptions.
class V8_EXPORT_PRIVATE ErrorThrower {
 public:
  ErrorThrower(Isolate* isolate, const char* context)
      : isolate_(isolate), context_(context) {}
  // Explicitly allow move-construction. Disallow copy (below).
  ErrorThrower(ErrorThrower&& other);
  ~ErrorThrower();

  PRINTF_FORMAT(2, 3) void TypeError(const char* fmt, ...);
  PRINTF_FORMAT(2, 3) void RangeError(const char* fmt, ...);
  PRINTF_FORMAT(2, 3) void CompileError(const char* fmt, ...);
  PRINTF_FORMAT(2, 3) void LinkError(const char* fmt, ...);
  PRINTF_FORMAT(2, 3) void RuntimeError(const char* fmt, ...);

  template <typename T>
  void CompileFailed(const char* error, Result<T>& result) {
    DCHECK(result.failed());
    CompileError("%s: %s @+%u", error, result.error_msg().c_str(),
                 result.error_offset());
  }

  // Create and return exception object.
  MUST_USE_RESULT Handle<Object> Reify();

  // Reset any error which was set on this thrower.
  void Reset();

  bool error() const { return error_type_ != kNone; }
  bool wasm_error() { return error_type_ >= kFirstWasmError; }

  Isolate* isolate() const { return isolate_; }

 private:
  enum ErrorType {
    kNone,
    // General errors.
    kTypeError,
    kRangeError,
    // Wasm errors.
    kCompileError,
    kLinkError,
    kRuntimeError,

    // Marker.
    kFirstWasmError = kCompileError
  };

  void Format(ErrorType error_type_, const char* fmt, va_list);

  Isolate* isolate_;
  const char* context_;
  ErrorType error_type_ = kNone;
  std::string error_msg_;

  DISALLOW_COPY_AND_ASSIGN(ErrorThrower);
  // ErrorThrower should always be stack-allocated, since it constitutes a scope
  // (things happen in the destructor).
  DISALLOW_NEW_AND_DELETE();
};

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

#endif
