// Copyright 2018 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_CODEGEN_HANDLER_TABLE_H_
#define V8_CODEGEN_HANDLER_TABLE_H_

#include "src/base/bit-field.h"
#include "src/common/assert-scope.h"
#include "src/common/globals.h"

namespace v8 {
namespace internal {

class Assembler;
class ByteArray;
class BytecodeArray;

// HandlerTable is a byte array containing entries for exception handlers in
// the code object it is associated with. The tables come in two flavors:
// 1) Based on ranges: Used for unoptimized code. Stored in a {ByteArray} that
//    is attached to each {BytecodeArray}. Contains one entry per exception
//    handler and a range representing the try-block covered by that handler.
//    Layout looks as follows:
//      [ range-start , range-end , handler-offset , handler-data ]
// 2) Based on return addresses: Used for turbofanned code. Stored directly in
//    the instruction stream of the {Code} object. Contains one entry per
//    call-site that could throw an exception. Layout looks as follows:
//      [ return-address-offset , handler-offset ]
class V8_EXPORT_PRIVATE HandlerTable {
 public:
  // Conservative prediction whether a given handler will locally catch an
  // exception or cause a re-throw to outside the code boundary. Since this is
  // undecidable it is merely an approximation (e.g. useful for debugger).
  enum CatchPrediction {
    UNCAUGHT,     // The handler will (likely) rethrow the exception.
    CAUGHT,       // The exception will be caught by the handler.
    PROMISE,      // The exception will be caught and cause a promise rejection.
    DESUGARING,   // The exception will be caught, but both the exception and
                  // the catching are part of a desugaring and should therefore
                  // not be visible to the user (we won't notify the debugger of
                  // such exceptions).
    ASYNC_AWAIT,  // The exception will be caught and cause a promise rejection
                  // in the desugaring of an async function, so special
                  // async/await handling in the debugger can take place.
    UNCAUGHT_ASYNC_AWAIT,  // The exception will be caught and cause a promise
                           // rejection in the desugaring of an async REPL
                           // script. The corresponding message object needs to
                           // be kept alive on the Isolate though.
  };

  enum EncodingMode { kRangeBasedEncoding, kReturnAddressBasedEncoding };

  // Constructors for the various encodings.
  explicit HandlerTable(Code code);
  explicit HandlerTable(ByteArray byte_array);
  explicit HandlerTable(BytecodeArray bytecode_array);
  HandlerTable(Address handler_table, int handler_table_size,
               EncodingMode encoding_mode);

  // Getters for handler table based on ranges.
  int GetRangeStart(int index) const;
  int GetRangeEnd(int index) const;
  int GetRangeHandler(int index) const;
  int GetRangeData(int index) const;

  // Setters for handler table based on ranges.
  void SetRangeStart(int index, int value);
  void SetRangeEnd(int index, int value);
  void SetRangeHandler(int index, int offset, CatchPrediction pred);
  void SetRangeData(int index, int value);

  // Returns the required length of the underlying byte array.
  static int LengthForRange(int entries);

  // Emitters for handler table based on return addresses.
  static int EmitReturnTableStart(Assembler* masm);
  static void EmitReturnEntry(Assembler* masm, int offset, int handler);

  // Lookup handler in a table based on ranges. The {pc_offset} is an offset to
  // the start of the potentially throwing instruction (using return addresses
  // for this value would be invalid).
  int LookupRange(int pc_offset, int* data, CatchPrediction* prediction);

  // Lookup handler in a table based on return addresses.
  int LookupReturn(int pc_offset);

  // Returns the number of entries in the table.
  int NumberOfRangeEntries() const;
  int NumberOfReturnEntries() const;

#ifdef ENABLE_DISASSEMBLER
  void HandlerTableRangePrint(std::ostream& os);   // NOLINT
  void HandlerTableReturnPrint(std::ostream& os);  // NOLINT
#endif

 private:
  // Getters for handler table based on ranges.
  CatchPrediction GetRangePrediction(int index) const;

  // Gets entry size based on mode.
  static int EntrySizeFromMode(EncodingMode mode);

  // Getters for handler table based on return addresses.
  int GetReturnOffset(int index) const;
  int GetReturnHandler(int index) const;

  // Number of entries in the loaded handler table.
  int number_of_entries_;

#ifdef DEBUG
  // The encoding mode of the table. Mostly useful for debugging to check that
  // used accessors and constructors fit together.
  EncodingMode mode_;
#endif

  // Direct pointer into the encoded data. This pointer points into objects on
  // the GC heap (either {ByteArray} or {Code}) and hence would become stale
  // during a collection. Hence we disallow any allocation.
  Address raw_encoded_data_;
  DISALLOW_HEAP_ALLOCATION(no_gc_)

  // Layout description for handler table based on ranges.
  static const int kRangeStartIndex = 0;
  static const int kRangeEndIndex = 1;
  static const int kRangeHandlerIndex = 2;
  static const int kRangeDataIndex = 3;
  static const int kRangeEntrySize = 4;

  // Layout description for handler table based on return addresses.
  static const int kReturnOffsetIndex = 0;
  static const int kReturnHandlerIndex = 1;
  static const int kReturnEntrySize = 2;

  // Encoding of the {handler} field.
  using HandlerPredictionField = base::BitField<CatchPrediction, 0, 3>;
  using HandlerOffsetField = base::BitField<int, 3, 29>;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_CODEGEN_HANDLER_TABLE_H_
