// Copyright 2006-2008 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.

// The infrastructure used for (localized) message reporting in V8.
//
// Note: there's a big unresolved issue about ownership of the data
// structures used by this framework.

#ifndef V8_MESSAGES_H_
#define V8_MESSAGES_H_

#include <memory>

#include "src/handles.h"
#include "src/message-template.h"

namespace v8 {
namespace internal {
namespace wasm {
class WasmCode;
}

// Forward declarations.
class AbstractCode;
class FrameArray;
class JSMessageObject;
class LookupIterator;
class SharedFunctionInfo;
class SourceInfo;
class WasmInstanceObject;

class MessageLocation {
 public:
  MessageLocation(Handle<Script> script, int start_pos, int end_pos);
  MessageLocation(Handle<Script> script, int start_pos, int end_pos,
                  Handle<SharedFunctionInfo> shared);
  MessageLocation();

  Handle<Script> script() const { return script_; }
  int start_pos() const { return start_pos_; }
  int end_pos() const { return end_pos_; }
  Handle<SharedFunctionInfo> shared() const { return shared_; }

 private:
  Handle<Script> script_;
  int start_pos_;
  int end_pos_;
  Handle<SharedFunctionInfo> shared_;
};

class StackFrameBase {
 public:
  virtual ~StackFrameBase() = default;

  virtual Handle<Object> GetReceiver() const = 0;
  virtual Handle<Object> GetFunction() const = 0;

  virtual Handle<Object> GetFileName() = 0;
  virtual Handle<Object> GetFunctionName() = 0;
  virtual Handle<Object> GetScriptNameOrSourceUrl() = 0;
  virtual Handle<Object> GetMethodName() = 0;
  virtual Handle<Object> GetTypeName() = 0;
  virtual Handle<Object> GetEvalOrigin();

  // Returns the script ID if one is attached, -1 otherwise.
  int GetScriptId() const;

  virtual int GetPosition() const = 0;
  // Return 1-based line number, including line offset.
  virtual int GetLineNumber() = 0;
  // Return 1-based column number, including column offset if first line.
  virtual int GetColumnNumber() = 0;

  // Returns index for Promise.all() async frames, or -1 for other frames.
  virtual int GetPromiseIndex() const = 0;

  virtual bool IsNative() = 0;
  virtual bool IsToplevel() = 0;
  virtual bool IsEval();
  virtual bool IsAsync() const = 0;
  virtual bool IsPromiseAll() const = 0;
  virtual bool IsConstructor() = 0;
  virtual bool IsStrict() const = 0;

  virtual MaybeHandle<String> ToString() = 0;

  // Used to signal that the requested field is unknown.
  static const int kNone = -1;

 protected:
  StackFrameBase() = default;
  explicit StackFrameBase(Isolate* isolate) : isolate_(isolate) {}
  Isolate* isolate_;

 private:
  virtual bool HasScript() const = 0;
  virtual Handle<Script> GetScript() const = 0;
};

class JSStackFrame : public StackFrameBase {
 public:
  JSStackFrame(Isolate* isolate, Handle<Object> receiver,
               Handle<JSFunction> function, Handle<AbstractCode> code,
               int offset);
  ~JSStackFrame() override = default;

  Handle<Object> GetReceiver() const override { return receiver_; }
  Handle<Object> GetFunction() const override;

  Handle<Object> GetFileName() override;
  Handle<Object> GetFunctionName() override;
  Handle<Object> GetScriptNameOrSourceUrl() override;
  Handle<Object> GetMethodName() override;
  Handle<Object> GetTypeName() override;

  int GetPosition() const override;
  int GetLineNumber() override;
  int GetColumnNumber() override;

  int GetPromiseIndex() const override;

  bool IsNative() override;
  bool IsToplevel() override;
  bool IsAsync() const override { return is_async_; }
  bool IsPromiseAll() const override { return is_promise_all_; }
  bool IsConstructor() override { return is_constructor_; }
  bool IsStrict() const override { return is_strict_; }

  MaybeHandle<String> ToString() override;

 private:
  JSStackFrame() = default;
  void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);

  bool HasScript() const override;
  Handle<Script> GetScript() const override;

  Handle<Object> receiver_;
  Handle<JSFunction> function_;
  Handle<AbstractCode> code_;
  int offset_;

  bool is_async_ : 1;
  bool is_constructor_ : 1;
  bool is_promise_all_ : 1;
  bool is_strict_ : 1;

  friend class FrameArrayIterator;
};

class WasmStackFrame : public StackFrameBase {
 public:
  ~WasmStackFrame() override = default;

  Handle<Object> GetReceiver() const override;
  Handle<Object> GetFunction() const override;

  Handle<Object> GetFileName() override { return Null(); }
  Handle<Object> GetFunctionName() override;
  Handle<Object> GetScriptNameOrSourceUrl() override { return Null(); }
  Handle<Object> GetMethodName() override { return Null(); }
  Handle<Object> GetTypeName() override { return Null(); }

  int GetPosition() const override;
  int GetLineNumber() override { return wasm_func_index_; }
  int GetColumnNumber() override { return kNone; }

  int GetPromiseIndex() const override { return kNone; }

  bool IsNative() override { return false; }
  bool IsToplevel() override { return false; }
  bool IsAsync() const override { return false; }
  bool IsPromiseAll() const override { return false; }
  bool IsConstructor() override { return false; }
  bool IsStrict() const override { return false; }
  bool IsInterpreted() const { return code_ == nullptr; }

  MaybeHandle<String> ToString() override;

 protected:
  Handle<Object> Null() const;

  bool HasScript() const override;
  Handle<Script> GetScript() const override;

  Handle<WasmInstanceObject> wasm_instance_;
  uint32_t wasm_func_index_;
  wasm::WasmCode* code_;  // null for interpreted frames.
  int offset_;

 private:
  WasmStackFrame() = default;
  void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);

  friend class FrameArrayIterator;
  friend class AsmJsWasmStackFrame;
};

class AsmJsWasmStackFrame : public WasmStackFrame {
 public:
  ~AsmJsWasmStackFrame() override = default;

  Handle<Object> GetReceiver() const override;
  Handle<Object> GetFunction() const override;

  Handle<Object> GetFileName() override;
  Handle<Object> GetScriptNameOrSourceUrl() override;

  int GetPosition() const override;
  int GetLineNumber() override;
  int GetColumnNumber() override;

  MaybeHandle<String> ToString() override;

 private:
  friend class FrameArrayIterator;
  AsmJsWasmStackFrame() = default;
  void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);

  bool is_at_number_conversion_;
};

class FrameArrayIterator {
 public:
  FrameArrayIterator(Isolate* isolate, Handle<FrameArray> array,
                     int frame_ix = 0);

  StackFrameBase* Frame();

  bool HasFrame() const;
  void Advance();

 private:
  Isolate* isolate_;

  Handle<FrameArray> array_;
  int frame_ix_;

  WasmStackFrame wasm_frame_;
  AsmJsWasmStackFrame asm_wasm_frame_;
  JSStackFrame js_frame_;
};

// Determines how stack trace collection skips frames.
enum FrameSkipMode {
  // Unconditionally skips the first frame. Used e.g. when the Error constructor
  // is called, in which case the first frame is always a BUILTIN_EXIT frame.
  SKIP_FIRST,
  // Skip all frames until a specified caller function is seen.
  SKIP_UNTIL_SEEN,
  SKIP_NONE,
};

class ErrorUtils : public AllStatic {
 public:
  static MaybeHandle<Object> Construct(
      Isolate* isolate, Handle<JSFunction> target, Handle<Object> new_target,
      Handle<Object> message, FrameSkipMode mode, Handle<Object> caller,
      bool suppress_detailed_trace);

  static MaybeHandle<String> ToString(Isolate* isolate, Handle<Object> recv);

  static MaybeHandle<Object> MakeGenericError(
      Isolate* isolate, Handle<JSFunction> constructor, MessageTemplate index,
      Handle<Object> arg0, Handle<Object> arg1, Handle<Object> arg2,
      FrameSkipMode mode);

  // Formats a textual stack trace from the given structured stack trace.
  // Note that this can call arbitrary JS code through Error.prepareStackTrace.
  static MaybeHandle<Object> FormatStackTrace(Isolate* isolate,
                                              Handle<JSObject> error,
                                              Handle<Object> stack_trace);
};

class MessageFormatter {
 public:
  static const char* TemplateString(MessageTemplate index);

  static MaybeHandle<String> FormatMessage(Isolate* isolate,
                                           MessageTemplate index,
                                           Handle<String> arg0,
                                           Handle<String> arg1,
                                           Handle<String> arg2);

  static Handle<String> FormatMessage(Isolate* isolate, MessageTemplate index,
                                      Handle<Object> arg);
};


// A message handler is a convenience interface for accessing the list
// of message listeners registered in an environment
class MessageHandler {
 public:
  // Returns a message object for the API to use.
  static Handle<JSMessageObject> MakeMessageObject(
      Isolate* isolate, MessageTemplate type, const MessageLocation* location,
      Handle<Object> argument, Handle<FixedArray> stack_frames);

  // Report a formatted message (needs JS allocation).
  static void ReportMessage(Isolate* isolate, const MessageLocation* loc,
                            Handle<JSMessageObject> message);

  static void DefaultMessageReport(Isolate* isolate, const MessageLocation* loc,
                                   Handle<Object> message_obj);
  static Handle<String> GetMessage(Isolate* isolate, Handle<Object> data);
  static std::unique_ptr<char[]> GetLocalizedMessage(Isolate* isolate,
                                                     Handle<Object> data);

 private:
  static void ReportMessageNoExceptions(Isolate* isolate,
                                        const MessageLocation* loc,
                                        Handle<Object> message_obj,
                                        v8::Local<v8::Value> api_exception_obj);
};


}  // namespace internal
}  // namespace v8

#endif  // V8_MESSAGES_H_
