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

// JSReceiver corresponds to objects in the JS sense.
@abstract
@highestInstanceTypeWithinParentClassRange
extern class JSReceiver extends HeapObject {
  properties_or_hash: SwissNameDictionary|FixedArrayBase|PropertyArray|Smi;
}

type Constructor extends JSReceiver;

@apiExposedInstanceTypeValue(0x421)
@highestInstanceTypeWithinParentClassRange
extern class JSObject extends JSReceiver {
  // [elements]: The elements (properties with names that are integers).
  //
  // Elements can be in two general modes: fast and slow. Each mode
  // corresponds to a set of object representations of elements that
  // have something in common.
  //
  // In the fast mode elements is a FixedArray and so each element can be
  // quickly accessed. The elements array can have one of several maps in this
  // mode: fixed_array_map, fixed_double_array_map,
  // sloppy_arguments_elements_map or fixed_cow_array_map (for copy-on-write
  // arrays). In the latter case the elements array may be shared by a few
  // objects and so before writing to any element the array must be copied. Use
  // EnsureWritableFastElements in this case.
  //
  // In the slow mode the elements is either a NumberDictionary or a
  // FixedArray parameter map for a (sloppy) arguments object.
  elements: FixedArrayBase;
}

macro NewJSObject(implicit context: Context)(): JSObject {
  const objectFunction: JSFunction = GetObjectFunction();
  const map: Map = Cast<Map>(objectFunction.prototype_or_initial_map)
      otherwise unreachable;
  return AllocateJSObjectFromMap(map);
}

extern class JSExternalObject extends JSObject {
  value: ExternalPointer;
}

// A JSObject that may contain EmbedderDataSlots for purposes other than being
// an API wrapper object. E.g., Promise objects can be set up to have embedder
// fields.
extern class JSObjectWithEmbedderSlots extends JSObject {}

// A JSObject that may contain EmbedderDataSlots and are considered API wrapper
// objects.
@abstract
extern class JSAPIObjectWithEmbedderSlots extends JSObject {
  cpp_heap_wrappable: CppHeapPointer;
}

@abstract
@lowestInstanceTypeWithinParentClassRange
extern class JSCustomElementsObject extends JSObject {}

// These may also contain EmbedderDataSlots but can't be a child class of
// JSAPIObjectWithEmbedderSlots due to type id constraints. These objects are
// also considered API wrapper objects.
@abstract
@lowestInstanceTypeWithinParentClassRange
extern class JSSpecialObject extends JSCustomElementsObject {
  // Mirror the same class hierarchy as with JSAPIObjectWithEmbedderSlots.
  cpp_heap_wrappable: CppHeapPointer;
}

macro GetDerivedMap(
    implicit context: Context)(target: JSFunction,
    newTarget: JSReceiver): Map {
  try {
    const constructor =
        Cast<JSFunctionWithPrototypeSlot>(newTarget) otherwise SlowPath;
    dcheck(IsConstructor(constructor));
    if (Is<TheHole>(constructor.prototype_or_initial_map)) goto SlowPath;
    const map =
        Cast<Map>(constructor.prototype_or_initial_map) otherwise SlowPath;
    if (LoadConstructorOrBackPointer(map) != target) {
      goto SlowPath;
    }

    return map;
  } label SlowPath {
    return runtime::GetDerivedMap(context, target, newTarget, FalseConstant());
  }
}

macro GetDerivedRabGsabTypedArrayMap(
    implicit context: Context)(target: JSFunction,
    newTarget: JSReceiver): Map {
  return runtime::GetDerivedMap(context, target, newTarget, TrueConstant());
}

macro AllocateFastOrSlowJSObjectFromMap(
    implicit context: Context)(map: Map): JSObject {
  let properties: EmptyFixedArray|NameDictionary|SwissNameDictionary =
      kEmptyFixedArray;
  if (IsDictionaryMap(map)) {
    @if(V8_ENABLE_SWISS_NAME_DICTIONARY) {
      properties =
          AllocateSwissNameDictionary(kSwissNameDictionaryInitialCapacity);
    }
    @ifnot(V8_ENABLE_SWISS_NAME_DICTIONARY) {
      properties = AllocateNameDictionary(kNameDictionaryInitialCapacity);
    }
  }
  return AllocateJSObjectFromMap(
      map, properties, kEmptyFixedArray, AllocationFlag::kNone,
      SlackTrackingMode::kWithSlackTracking);
}

extern class JSGlobalProxy extends JSSpecialObject {}

extern class JSGlobalObject extends JSSpecialObject {
  // [global proxy]: the global proxy object of the current context.
  // This global proxy preserves identity of the JavaScript's global object
  // even if the context and global object associated with it is detached.
  // So, in case of detached context, the
  //   |context->global_object->global_proxy->global_object|
  // is not equal to
  //   |context->global_object|
  // anymore.
  global_proxy: JSGlobalProxy;

  // [global_proxy_for_api]: the global proxy object that points back to
  // this global object even if the context is detached.
  // As long as the context is not detached it's the same as [global_proxy],
  // however when a context is detached a new JSGlobalProxy is created which
  // reuses the original JSGlobalProxy's map, but all embedder pointers and
  // API wrappers are cleared. Api callbacks can still be triggered for
  // detached global object via contextual accesses and this object is
  // supposed be passed as the holder to Api callbacks.
  // Thus, this always holds:
  //   |context->global_object->global_proxy_for_api->global_object|
  // is equal to
  //   |context->global_object|.
  global_proxy_for_api: JSGlobalProxy;
}

extern class JSPrimitiveWrapper extends JSCustomElementsObject {
  value: JSAny;
}

extern class JSMessageObject extends JSObject {
  // Tagged fields.
  message_type: Smi;
  // [argument]: the arguments for formatting the error message.
  argument: Object;
  // [script]: the script from which the error message originated.
  script: Script;
  // [stack_trace]: a StackTraceInfo for this error object.
  stack_trace: StackTraceInfo|TheHole;
  shared_info: SharedFunctionInfo|Smi;

  // Raw data fields.
  // TODO(ishell): store as int32 instead of Smi.
  bytecode_offset: Smi;
  start_position: Smi;
  end_position: Smi;
  error_level: Smi;
}

extern class JSDate extends JSObject {
  // If one component is NaN, all of them are, indicating a NaN time value.

  // The time value.
  value: float64;

  // Cached values:
  year: Smi|NaN;
  month: Smi|NaN;
  day: Smi|NaN;
  weekday: Smi|NaN;
  hour: Smi|NaN;
  min: Smi|NaN;
  sec: Smi|NaN;

  // Sample of the date cache stamp at the moment when chached fields were
  // cached.
  cache_stamp: Smi|NaN;
}

extern class JSAsyncFromSyncIterator extends JSObject {
  sync_iterator: JSReceiver;
  // The "next" method is loaded during GetIterator, and is not reloaded for
  // subsequent "next" invocations.
  next: Object;
}

extern class JSStringIterator extends JSObject {
  // The [[IteratedString]] slot.
  string: String;
  // The [[StringIteratorNextIndex]] slot.
  index: Smi;
}

// The wrapper returned by Iterator.from().
// https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype-object
extern class JSValidIteratorWrapper extends JSObject {
  // The [[Iterated]] slot.
  underlying: iterator::IteratorRecord;
}

extern macro AllocateJSObjectFromMap(Map): JSObject;
extern macro AllocateJSObjectFromMap(
    Map,
    NameDictionary|SwissNameDictionary|EmptyFixedArray|PropertyArray): JSObject;
extern macro AllocateJSObjectFromMap(
    Map, NameDictionary|SwissNameDictionary|EmptyFixedArray|PropertyArray,
    FixedArray, constexpr AllocationFlag,
    constexpr SlackTrackingMode): JSObject;