// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be:
// Context found in the LICENSE file.

#include 'src/builtins/builtins-regexp-gen.h'
#include 'src/builtins/builtins-utils-gen.h'
#include 'src/builtins/builtins.h'
#include 'src/codegen/code-factory.h'
#include 'src/heap/factory-inl.h'
#include 'src/objects/arguments.h'
#include 'src/objects/bigint.h'
#include 'src/objects/elements-kind.h'
#include 'src/objects/free-space.h'
#include 'src/objects/js-function.h'
#include 'src/objects/js-generator.h'
#include 'src/objects/js-promise.h'
#include 'src/objects/js-regexp-string-iterator.h'
#include 'src/objects/js-weak-refs.h'
#include 'src/objects/objects.h'
#include 'src/objects/source-text-module.h'
#include 'src/objects/stack-frame-info.h'
#include 'src/objects/synthetic-module.h'
#include 'src/objects/template-objects.h'
#include 'src/torque/runtime-support.h'

type void;
type never;

type Tagged generates 'TNode<MaybeObject>' constexpr 'MaybeObject';
type StrongTagged extends Tagged
    generates 'TNode<Object>' constexpr 'Object';
type Smi extends StrongTagged generates 'TNode<Smi>' constexpr 'Smi';
type TaggedIndex extends StrongTagged
    generates 'TNode<TaggedIndex>' constexpr 'TaggedIndex';
// A possibly cleared weak pointer with a bit pattern that distinguishes it from
// strong HeapObject pointers and Smi values.
type WeakHeapObject extends Tagged;
type Weak<T : type extends HeapObject> extends WeakHeapObject;

type Object = Smi|HeapObject;
type MaybeObject = Smi|HeapObject|WeakHeapObject;

// A Smi that is greater than or equal to 0. See TaggedIsPositiveSmi.
type PositiveSmi extends Smi;

// The Smi value zero, which is often used as null for HeapObject types.
type Zero extends PositiveSmi;
// A tagged value represented by an all-zero bitpattern.
type TaggedZeroPattern extends TaggedIndex;

// A value with the size of Tagged which may contain arbitrary data.
type Uninitialized extends Tagged;

extern macro MakeWeak(HeapObject): WeakHeapObject;
extern macro GetHeapObjectAssumeWeak(MaybeObject): HeapObject labels IfCleared;
extern macro GetHeapObjectIfStrong(MaybeObject): HeapObject labels IfNotStrong;
extern macro IsWeakOrCleared(MaybeObject): bool;
extern macro IsWeakReferenceToObject(MaybeObject, Object): bool;
extern macro IsStrong(MaybeObject): bool;

macro StrongToWeak<T: type>(x: T): Weak<T> {
  return %RawDownCast<Weak<T>>(MakeWeak(x));
}
macro WeakToStrong<T: type>(x: Weak<T>): T labels ClearedWeakPointer {
  const x = GetHeapObjectAssumeWeak(x) otherwise ClearedWeakPointer;
  return %RawDownCast<T>(x);
}

// Defined to coincide with https://tc39.es/ecma262/#sec-ispropertykey
// Doesn't include PrivateSymbol.
type PropertyKey = String|PublicSymbol;

// TODO(turbofan): PrivateSymbol is only exposed to JavaScript through the
// debugger API. We should reconsider this and try not to expose it at all. Then
// JSAny would not need to contain it.

// A JavaScript primitive value as defined in
// https://tc39.es/ecma262/#sec-primitive-value.
type JSPrimitive = Numeric|String|Symbol|Boolean|Null|Undefined;

// A user-exposed JavaScript value, as opposed to V8-internal values like
// TheHole or FixedArray.
type JSAny = JSReceiver|JSPrimitive;

type JSAnyNotNumeric = String|Symbol|Boolean|Null|Undefined|JSReceiver;
type JSAnyNotNumber = BigInt|JSAnyNotNumeric;

// This is the intersection of JSAny and HeapObject.
type JSAnyNotSmi = JSAnyNotNumber|HeapNumber;

type int32 generates 'TNode<Int32T>' constexpr 'int32_t';
type uint32 generates 'TNode<Uint32T>' constexpr 'uint32_t';
type int31 extends int32
    generates 'TNode<Int32T>' constexpr 'int31_t';
type uint31 extends uint32
    generates 'TNode<Uint32T>' constexpr 'uint32_t';
type int16 extends int31
    generates 'TNode<Int16T>' constexpr 'int16_t';
type uint16 extends uint31
    generates 'TNode<Uint16T>' constexpr 'uint16_t';
type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t';
type uint8 extends uint16
    generates 'TNode<Uint8T>' constexpr 'uint8_t';
type char8 extends uint8 constexpr 'char';
type char16 extends uint16 constexpr 'char16_t';
type int64 generates 'TNode<Int64T>' constexpr 'int64_t';
type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
type uintptr generates 'TNode<UintPtrT>' constexpr 'uintptr_t';
type float32 generates 'TNode<Float32T>' constexpr 'float';
type float64 generates 'TNode<Float64T>' constexpr 'double';
type bool generates 'TNode<BoolT>' constexpr 'bool';
type bint generates 'TNode<BInt>' constexpr 'BInt';
type string constexpr 'const char*';

// A Smi value containing a bitfield struct as its integer data.
@useParentTypeChecker type SmiTagged<T : type extends uint31> extends Smi;

// WARNING: The memory representation (i.e., in class fields and arrays) of
// float64_or_hole is just a float64 that may be the hole-representing
// signalling NaN bit-pattern. So it's memory size is that of float64 and
// loading and storing float64_or_hole emits special code.
struct float64_or_hole {
  macro Value(): float64 labels IfHole {
    if (this.is_hole) {
      goto IfHole;
    }
    return this.value;
  }
  macro ValueUnsafeAssumeNotHole(): float64 {
    assert(!this.is_hole);
    return this.value;
  }

  is_hole: bool;
  value: float64;
}
const kDoubleHole: float64_or_hole = float64_or_hole{is_hole: true, value: 0};

@doNotGenerateCast
@abstract
extern class JSPrototype extends JSObject generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSObjectPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSRegExpPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSPromisePrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSTypedArrayPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSSetPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSIteratorPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSArrayIteratorPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSMapIteratorPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSSetIteratorPrototype extends JSPrototype
    generates 'TNode<JSObject>';
@doNotGenerateCast
extern class JSStringIteratorPrototype extends JSPrototype
    generates 'TNode<JSObject>';

// The HashTable inheritance hierarchy doesn't actually look like this in C++
// because it uses some class templates that we can't yet (and may never)
// express in Torque, but this is the expected organization of instance types.
@doNotGenerateCast
extern class HashTable extends FixedArray generates 'TNode<FixedArray>';
extern class OrderedHashMap extends HashTable;
extern class OrderedHashSet extends HashTable;
extern class OrderedNameDictionary extends HashTable;
extern class NameDictionary extends HashTable;
extern class GlobalDictionary extends HashTable;
extern class SimpleNumberDictionary extends HashTable;
extern class EphemeronHashTable extends HashTable;
type ObjectHashTable extends HashTable
    generates 'TNode<ObjectHashTable>';
extern class NumberDictionary extends HashTable;

type RawPtr generates 'TNode<RawPtrT>' constexpr 'Address';
type RawPtr<To: type> extends RawPtr;
type ExternalPointer
    generates 'TNode<ExternalPointerT>' constexpr 'ExternalPointer_t';
extern class Code extends HeapObject;
type BuiltinPtr extends Smi generates 'TNode<BuiltinPtr>';

type Number = Smi|HeapNumber;
type Numeric = Number|BigInt;

extern class ObjectBoilerplateDescription extends FixedArray;
extern class ClosureFeedbackCellArray extends FixedArray;
extern class ScriptContextTable extends FixedArray;

type LayoutDescriptor extends ByteArray
    generates 'TNode<LayoutDescriptor>';
extern class TransitionArray extends WeakFixedArray;

extern operator '.length_intptr' macro LoadAndUntagWeakFixedArrayLength(
    WeakFixedArray): intptr;

type InstanceType extends uint16 constexpr 'InstanceType';

type NoSharedNameSentinel extends Smi;

// Specialized types. The following three type definitions don't correspond to
// actual C++ classes, but have Is... methods that check additional constraints.

// A Foreign object whose raw pointer is not allowed to be null.
type NonNullForeign extends Foreign;

// A function built with InstantiateFunction for the public API.
type CallableApiObject extends JSObject;

// A JSProxy with the callable bit set.
type CallableJSProxy extends JSProxy;

type Callable = JSFunction|JSBoundFunction|CallableJSProxy|CallableApiObject;

type WriteBarrierMode
    generates 'TNode<Int32T>' constexpr 'WriteBarrierMode';

extern enum UnicodeEncoding { UTF16, UTF32 }

// Promise constants
extern enum PromiseState extends int31 constexpr 'Promise::PromiseState' {
  kPending,
  kFulfilled,
  kRejected
}

type FrameArray extends FixedArray;

const kTaggedSize: constexpr int31 generates 'kTaggedSize';
const kDoubleSize: constexpr int31 generates 'kDoubleSize';
const kVariableSizeSentinel:
    constexpr int31 generates 'kVariableSizeSentinel';

const kSmiTagSize: constexpr int31 generates 'kSmiTagSize';
const kHeapObjectTag: constexpr int31 generates 'kHeapObjectTag';
const V8_INFINITY: constexpr float64 generates 'V8_INFINITY';
const MINUS_V8_INFINITY: constexpr float64 generates '-V8_INFINITY';

extern enum ElementsKind extends int32 {
  NO_ELEMENTS,

  PACKED_SMI_ELEMENTS,
  HOLEY_SMI_ELEMENTS,
  PACKED_ELEMENTS,
  HOLEY_ELEMENTS,
  PACKED_DOUBLE_ELEMENTS,
  HOLEY_DOUBLE_ELEMENTS,
  LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND,
  DICTIONARY_ELEMENTS,

  UINT8_ELEMENTS,
  INT8_ELEMENTS,
  UINT16_ELEMENTS,
  INT16_ELEMENTS,
  UINT32_ELEMENTS,
  INT32_ELEMENTS,
  FLOAT32_ELEMENTS,
  FLOAT64_ELEMENTS,
  UINT8_CLAMPED_ELEMENTS,
  BIGUINT64_ELEMENTS,
  BIGINT64_ELEMENTS,
  ...
}

extern enum AllocationFlag extends int32
constexpr 'CodeStubAssembler::AllocationFlag' {
  kNone,
  kDoubleAlignment,
  kPretenured,
  kAllowLargeObjectAllocation
}

extern enum SlackTrackingMode
constexpr 'CodeStubAssembler::SlackTrackingMode' {
  kWithSlackTracking,
  kNoSlackTracking
}

extern enum ExtractFixedArrayFlag
constexpr 'CodeStubAssembler::ExtractFixedArrayFlag' {
  kFixedDoubleArrays,
  kAllFixedArrays,
  kFixedArrays,
  ...
}

const kBigIntMaxLength: constexpr intptr generates 'BigInt::kMaxLength';

extern enum MessageTemplate {
  kAllPromisesRejected,
  kInvalidArrayBufferLength,
  kInvalidArrayLength,
  kInvalidIndex,
  kNotConstructor,
  kNotGeneric,
  kCalledNonCallable,
  kCalledOnNullOrUndefined,
  kCannotConvertToPrimitive,
  kProtoObjectOrNull,
  kInvalidOffset,
  kInvalidTypedArrayLength,
  kIteratorSymbolNonCallable,
  kIteratorValueNotAnObject,
  kNotIterable,
  kReduceNoInitial,
  kFirstArgumentNotRegExp,
  kBigIntMixedTypes,
  kTypedArrayTooShort,
  kTypedArrayTooLargeToSort,
  kInvalidCountValue,
  kConstructorNotFunction,
  kSymbolToString,
  kPropertyNotFunction,
  kBigIntTooBig,
  kNotTypedArray,
  kDetachedOperation,
  kBadSortComparisonFunction,
  kIncompatibleMethodReceiver,
  kInvalidDataViewAccessorOffset,
  kTypedArraySetOffsetOutOfBounds,
  kInvalidArgument,
  kInvalidRegExpExecResult,
  kRegExpNonRegExp,
  kRegExpNonObject,
  kPromiseNonCallable,
  kNotAPromise,
  kResolverNotAFunction,
  kTooManyElementsInPromiseCombinator,
  kToRadixFormatRange,
  kCalledOnNonObject,
  kRegExpGlobalInvokedOnNonGlobal,
  kProxyNonObject,
  kProxyRevoked,
  kProxyTrapReturnedFalsishFor,
  kProxyPrivate,
  kProxyIsExtensibleInconsistent,
  kProxyPreventExtensionsExtensible,
  kProxyTrapReturnedFalsish,
  kProxyGetPrototypeOfInvalid,
  kProxyGetPrototypeOfNonExtensible,
  kProxySetPrototypeOfNonExtensible,
  kProxyDeletePropertyNonExtensible,
  kUndefinedOrNullToObject,
  kWeakRefsCleanupMustBeCallable,
  kWasmTrapUnreachable,
  kWasmTrapMemOutOfBounds,
  kWasmTrapUnalignedAccess,
  kWasmTrapDivByZero,
  kWasmTrapDivUnrepresentable,
  kWasmTrapRemByZero,
  kWasmTrapFloatUnrepresentable,
  kWasmTrapFuncSigMismatch,
  kWasmTrapDataSegmentDropped,
  kWasmTrapElemSegmentDropped,
  kWasmTrapTableOutOfBounds,
  kWasmTrapRethrowNull,
  kWasmTrapNullDereference,
  kWasmTrapIllegalCast,
  kWasmTrapArrayOutOfBounds,
  kWeakRefsRegisterTargetAndHoldingsMustNotBeSame,
  kWeakRefsRegisterTargetMustBeObject,
  kWeakRefsUnregisterTokenMustBeObject,
  kWeakRefsWeakRefConstructorTargetMustBeObject,
  ...
}

extern enum PropertyAttributes extends int31 {
  NONE,
  READ_ONLY,
  DONT_ENUM,
  DONT_DELETE,
  ALL_ATTRIBUTES_MASK,
  FROZEN,
  ...
}

const kMaxArrayIndex:
    constexpr uint32 generates 'JSArray::kMaxArrayIndex';
const kArrayBufferMaxByteLength:
    constexpr uintptr generates 'JSArrayBuffer::kMaxByteLength';
const kTypedArrayMaxLength:
    constexpr uintptr generates 'JSTypedArray::kMaxLength';
const kMaxTypedArrayInHeap:
    constexpr int31 generates 'JSTypedArray::kMaxSizeInHeap';
// CSA does not support 64-bit types on 32-bit platforms so as a workaround the
// kMaxSafeIntegerUint64 is defined as uintptr and allowed to be used only
// inside if constexpr (Is64()) i.e. on 64-bit architectures.
const kMaxSafeIntegerUint64: constexpr uintptr
    generates 'CodeStubAssembler::MaxSafeIntegerUintPtr()';
const kMaxSafeInteger: constexpr float64 generates 'kMaxSafeInteger';
const kMaxUInt32Double: constexpr float64 generates 'kMaxUInt32Double';
const kSmiMaxValue: constexpr uintptr generates 'kSmiMaxValue';
const kSmiMax: uintptr = kSmiMaxValue;
// TODO(v8:8996): Use uintptr version instead and drop this one.
const kStringMaxLength: constexpr int31 generates 'String::kMaxLength';
const kStringMaxLengthUintptr:
    constexpr uintptr generates 'String::kMaxLength';
const kFixedArrayMaxLength:
    constexpr int31 generates 'FixedArray::kMaxLength';
const kFixedDoubleArrayMaxLength:
    constexpr int31 generates 'FixedDoubleArray::kMaxLength';
const kObjectAlignmentMask: constexpr intptr
    generates 'kObjectAlignmentMask';
const kMinAddedElementsCapacity:
    constexpr int31 generates 'JSObject::kMinAddedElementsCapacity';
const kMaxCopyElements:
    constexpr int31 generates 'JSArray::kMaxCopyElements';
const kMaxRegularHeapObjectSize: constexpr int31
    generates 'kMaxRegularHeapObjectSize';

const kMaxNewSpaceFixedArrayElements: constexpr int31
    generates 'FixedArray::kMaxRegularLength';

extern enum PrimitiveType { kString, kBoolean, kSymbol, kNumber }

const kNameDictionaryInitialCapacity:
    constexpr int32 generates 'NameDictionary::kInitialCapacity';
const kOrderedNameDictionaryInitialCapacity:
    constexpr int32 generates 'OrderedNameDictionary::kInitialCapacity';

const kWasmArrayHeaderSize:
    constexpr int32 generates 'WasmArray::kHeaderSize';

const kDictModePrototypes:
    constexpr bool generates 'V8_DICT_MODE_PROTOTYPES_BOOL';

type TheHole extends Oddball;
type Null extends Oddball;
type Undefined extends Oddball;
type True extends Oddball;
type False extends Oddball;
type Exception extends Oddball;
type EmptyString extends String;
type Boolean = True|False;

type NumberOrUndefined = Number|Undefined;

extern macro DefaultStringConstant(): String;
extern macro EmptyStringConstant(): EmptyString;
extern macro ErrorsStringConstant(): String;
extern macro FalseConstant(): False;
extern macro Int32FalseConstant(): bool;
extern macro Int32TrueConstant(): bool;
extern macro IteratorSymbolConstant(): PublicSymbol;
extern macro LengthStringConstant(): String;
extern macro MatchSymbolConstant(): Symbol;
extern macro MessageStringConstant(): String;
extern macro NanConstant(): NaN;
extern macro NameStringConstant(): String;
extern macro NullConstant(): Null;
extern macro NumberStringConstant(): String;
extern macro ReturnStringConstant(): String;
extern macro StringStringConstant(): String;
extern macro TheHoleConstant(): TheHole;
extern macro ToPrimitiveSymbolConstant(): PublicSymbol;
extern macro ToStringStringConstant(): String;
extern macro TrueConstant(): True;
extern macro UndefinedConstant(): Undefined;
extern macro ValueOfStringConstant(): String;
extern macro WasmWrappedObjectSymbolConstant(): Symbol;

const TheHole: TheHole = TheHoleConstant();
const Null: Null = NullConstant();
const Undefined: Undefined = UndefinedConstant();
const True: True = TrueConstant();
const False: False = FalseConstant();
const kEmptyString: EmptyString = EmptyStringConstant();
const kLengthString: String = LengthStringConstant();
const kMessageString: String = MessageStringConstant();
const kReturnString: String = ReturnStringConstant();

const kNaN: NaN = NanConstant();
const kZero: Zero = %RawDownCast<Zero>(SmiConstant(0));
const kZeroBitPattern: TaggedZeroPattern = %RawDownCast<TaggedZeroPattern>(
    Convert<Tagged>(BitcastWordToTaggedSigned(Convert<intptr>(0))));

const true: constexpr bool generates 'true';
const false: constexpr bool generates 'false';

extern enum LanguageMode extends bool { kStrict, kSloppy }
type LanguageModeSmi extends Smi;

const SKIP_WRITE_BARRIER:
    constexpr WriteBarrierMode generates 'SKIP_WRITE_BARRIER';
const UNSAFE_SKIP_WRITE_BARRIER:
    constexpr WriteBarrierMode generates 'UNSAFE_SKIP_WRITE_BARRIER';

extern transitioning macro AllocateJSIteratorResult(implicit context: Context)(
    JSAny, Boolean): JSObject;

extern class Filler extends HeapObject generates 'TNode<HeapObject>';

// Various logical subclasses of JSObject, which have their own instance types
// but not their own class definitions:

// Like JSObject, but created from API function.
@apiExposedInstanceTypeValue(0x420)
extern class JSApiObject extends JSObject generates 'TNode<JSObject>';
// Like JSApiObject, but requires access checks and/or has interceptors.
@apiExposedInstanceTypeValue(0x410)
extern class JSSpecialApiObject extends JSSpecialObject
    generates 'TNode<JSSpecialObject>';
extern class JSContextExtensionObject extends JSObject
    generates 'TNode<JSObject>';
extern class JSError extends JSObject generates 'TNode<JSObject>';

extern macro Is64(): constexpr bool;

extern macro SelectBooleanConstant(bool): Boolean;

extern macro Print(constexpr string);
extern macro Print(constexpr string, Object);
extern macro Comment(constexpr string);
extern macro Print(Object);
extern macro DebugBreak();

// ES6 7.1.4 ToInteger ( argument )
transitioning macro ToIntegerImpl(implicit context: Context)(input: JSAny):
    Number {
  let input = input;

  while (true) {
    typeswitch (input) {
      case (s: Smi): {
        return s;
      }
      case (hn: HeapNumber): {
        let value = Convert<float64>(hn);
        if (Float64IsNaN(value)) return SmiConstant(0);
        value = math::Float64Trunc(value);
        // ToInteger normalizes -0 to +0.
        if (value == 0.0) return SmiConstant(0);
        const result = ChangeFloat64ToTagged(value);
        assert(IsNumberNormalized(result));
        return result;
      }
      case (a: JSAnyNotNumber): {
        input = conversion::NonNumberToNumber(a);
      }
    }
  }
  unreachable;
}

transitioning builtin ToInteger(implicit context: Context)(input: JSAny):
    Number {
  return ToIntegerImpl(input);
}

@export
transitioning macro ToInteger_Inline(implicit context: Context)(input: JSAny):
    Number {
  typeswitch (input) {
    case (s: Smi): {
      return s;
    }
    case (JSAny): {
      return ToInteger(input);
    }
  }
}

extern enum BigIntHandling extends int32
constexpr 'CodeStubAssembler::BigIntHandling' { kConvertToNumber, kThrow }

extern transitioning macro ToNumber(implicit context: Context)(
    JSAny, constexpr BigIntHandling): Number;

extern transitioning macro ToLength_Inline(implicit context: Context)(JSAny):
    Number;
extern transitioning macro ToNumber_Inline(implicit context: Context)(JSAny):
    Number;
extern transitioning macro ToString_Inline(implicit context: Context)(JSAny):
    String;
extern transitioning macro ToThisString(implicit context: Context)(
    JSAny, String): String;
extern transitioning macro ToThisValue(implicit context: Context)(
    JSAny, constexpr PrimitiveType, constexpr string): JSAny;
extern transitioning macro GetProperty(implicit context: Context)(
    JSAny, JSAny): JSAny;
extern transitioning builtin SetProperty(implicit context: Context)(
    JSAny, JSAny, JSAny): JSAny;
extern transitioning builtin SetPropertyIgnoreAttributes(
    implicit context: Context)(JSObject, String, JSAny, Smi): JSAny;
extern transitioning builtin SetPropertyInLiteral(implicit context: Context)(
    JSAny, JSAny, JSAny): JSAny;
extern transitioning builtin DeleteProperty(implicit context: Context)(
    JSAny, JSAny | PrivateSymbol, LanguageModeSmi): Boolean;
extern transitioning builtin HasProperty(implicit context: Context)(
    JSAny, JSAny): Boolean;
extern transitioning macro HasProperty_Inline(implicit context: Context)(
    JSReceiver, JSAny): Boolean;
extern builtin LoadIC(
    Context, JSAny, JSAny, TaggedIndex, FeedbackVector): JSAny;

extern macro SetPropertyStrict(Context, Object, Object, Object): Object;

extern macro ThrowRangeError(implicit context: Context)(
    constexpr MessageTemplate): never;
extern macro ThrowRangeError(implicit context: Context)(
    constexpr MessageTemplate, Object): never;
extern macro ThrowTypeError(implicit context: Context)(
    constexpr MessageTemplate): never;
extern macro ThrowTypeError(implicit context: Context)(
    constexpr MessageTemplate, constexpr string): never;
extern macro ThrowTypeError(implicit context: Context)(
    constexpr MessageTemplate, Object): never;
extern macro ThrowTypeError(implicit context: Context)(
    constexpr MessageTemplate, Object, Object): never;
extern macro ThrowTypeError(implicit context: Context)(
    constexpr MessageTemplate, Object, Object, Object): never;
extern transitioning runtime ThrowTypeErrorIfStrict(implicit context: Context)(
    Smi, Object, Object): void;
extern transitioning runtime ThrowCalledNonCallable(implicit context: Context)(
    JSAny): never;

extern transitioning macro ThrowIfNotJSReceiver(implicit context: Context)(
    JSAny, constexpr MessageTemplate, constexpr string): void;

extern macro ArraySpeciesCreate(Context, JSAny, Number): JSReceiver;
extern macro ArrayCreate(implicit context: Context)(Number): JSArray;
extern macro BuildAppendJSArray(
    constexpr ElementsKind, FastJSArray, JSAny): void labels Bailout;

extern macro EnsureArrayPushable(implicit context: Context)(Map): ElementsKind
    labels Bailout;
// TODO: Reduce duplication once varargs are supported in macros.
extern macro Construct(implicit context: Context)(Constructor): JSReceiver;
extern macro Construct(implicit context: Context)(
    Constructor, JSAny): JSReceiver;
extern macro Construct(implicit context: Context)(
    Constructor, JSAny, JSAny): JSReceiver;
extern macro Construct(implicit context: Context)(
    Constructor, JSAny, JSAny, JSAny): JSReceiver;
extern macro ConstructWithTarget(implicit context: Context)(
    Constructor, JSReceiver): JSReceiver;
extern macro ConstructWithTarget(implicit context: Context)(
    Constructor, JSReceiver, JSAny): JSReceiver;
extern macro SpeciesConstructor(implicit context: Context)(
    JSAny, JSReceiver): JSReceiver;

extern macro ConstructorBuiltinsAssembler::IsDictionaryMap(Map): bool;
extern macro CodeStubAssembler::AllocateNameDictionary(constexpr int32):
    NameDictionary;
extern macro CodeStubAssembler::AllocateOrderedNameDictionary(constexpr int32):
    OrderedNameDictionary;

extern builtin ToObject(Context, JSAny): JSReceiver;
extern macro ToObject_Inline(Context, JSAny): JSReceiver;
extern macro IsUndefined(Object): bool;
extern macro IsNullOrUndefined(Object): bool;
extern macro IsString(HeapObject): bool;
extern transitioning builtin NonPrimitiveToPrimitive_String(
    Context, JSAny): JSPrimitive;
extern transitioning builtin NonPrimitiveToPrimitive_Default(
    Context, JSAny): JSPrimitive;

transitioning macro ToPrimitiveDefault(implicit context: Context)(v: JSAny):
    JSPrimitive {
  typeswitch (v) {
    case (v: JSReceiver): {
      return NonPrimitiveToPrimitive_Default(context, v);
    }
    case (v: JSPrimitive): {
      return v;
    }
  }
}

extern transitioning runtime NormalizeElements(Context, JSObject);
extern transitioning runtime TransitionElementsKindWithKind(
    Context, JSObject, Smi);

extern macro LoadBufferObject(RawPtr, constexpr int32): Object;
extern macro LoadBufferPointer(RawPtr, constexpr int32): RawPtr;
extern macro LoadBufferSmi(RawPtr, constexpr int32): Smi;
extern macro LoadBufferIntptr(RawPtr, constexpr int32): intptr;

extern runtime StringEqual(Context, String, String): Oddball;
extern builtin StringLessThan(Context, String, String): Boolean;
extern macro StringCharCodeAt(String, uintptr): char16;
extern macro StringFromSingleCharCode(char8): String;
extern macro StringFromSingleCharCode(char16): String;

extern macro NumberToString(Number): String;
extern macro StringToNumber(String): Number;
extern transitioning macro NonNumberToNumber(implicit context: Context)(
    JSAnyNotNumber): Number;
extern transitioning macro NonNumberToNumeric(implicit context: Context)(
    JSAnyNotNumber): Numeric;

extern macro Equal(JSAny, JSAny, Context): Boolean;
macro Equal(implicit context: Context)(left: JSAny, right: JSAny): Boolean {
  return Equal(left, right);
}

extern macro StrictEqual(JSAny, JSAny): Boolean;
extern macro SmiLexicographicCompare(Smi, Smi): Smi;
extern runtime ReThrow(Context, JSAny): never;
extern runtime Throw(implicit context: Context)(JSAny): never;
extern runtime ThrowInvalidStringLength(Context): never;

extern operator '==' macro WordEqual(RawPtr, RawPtr): bool;
extern operator '!=' macro WordNotEqual(RawPtr, RawPtr): bool;
extern operator '+' macro RawPtrAdd(RawPtr, intptr): RawPtr;
extern operator '+' macro RawPtrAdd(intptr, RawPtr): RawPtr;

extern operator '<' macro Int32LessThan(int32, int32): bool;
extern operator '<' macro Uint32LessThan(uint32, uint32): bool;
extern operator '>' macro Int32GreaterThan(int32, int32): bool;
extern operator '>' macro Uint32GreaterThan(uint32, uint32): bool;
extern operator '<=' macro Int32LessThanOrEqual(int32, int32): bool;
extern operator '<=' macro Uint32LessThanOrEqual(uint32, uint32): bool;
extern operator '>=' macro Int32GreaterThanOrEqual(int32, int32): bool;
extern operator '>=' macro Uint32GreaterThanOrEqual(uint32, uint32): bool;

extern operator '==' macro SmiEqual(Smi, Smi): bool;
extern operator '!=' macro SmiNotEqual(Smi, Smi): bool;
extern operator '<' macro SmiLessThan(Smi, Smi): bool;
extern operator '<=' macro SmiLessThanOrEqual(Smi, Smi): bool;
extern operator '>' macro SmiGreaterThan(Smi, Smi): bool;
extern operator '>=' macro SmiGreaterThanOrEqual(Smi, Smi): bool;

extern operator '==' macro ElementsKindEqual(
    constexpr ElementsKind, constexpr ElementsKind): constexpr bool;
extern operator '==' macro ElementsKindEqual(ElementsKind, ElementsKind): bool;
operator '!=' macro ElementsKindNotEqual(
    k1: ElementsKind, k2: ElementsKind): bool {
  return !ElementsKindEqual(k1, k2);
}
extern macro IsElementsKindLessThanOrEqual(
    ElementsKind, constexpr ElementsKind): bool;
extern macro IsElementsKindGreaterThan(
    ElementsKind, constexpr ElementsKind): bool;
extern macro IsElementsKindInRange(
    ElementsKind, constexpr ElementsKind, constexpr ElementsKind): bool;

extern macro IsFastElementsKind(constexpr ElementsKind): constexpr bool;
extern macro IsDoubleElementsKind(constexpr ElementsKind): constexpr bool;

extern macro IsFastAliasedArgumentsMap(implicit context: Context)(Map): bool;
extern macro IsSlowAliasedArgumentsMap(implicit context: Context)(Map): bool;
extern macro IsSloppyArgumentsMap(implicit context: Context)(Map): bool;
extern macro IsStrictArgumentsMap(implicit context: Context)(Map): bool;
extern macro IsTuple2Map(Map): bool;

extern macro SmiAbove(Smi, Smi): bool;

extern operator '==' macro WordEqual(intptr, intptr): bool;
extern operator '==' macro WordEqual(uintptr, uintptr): bool;
extern operator '!=' macro WordNotEqual(intptr, intptr): bool;
extern operator '!=' macro WordNotEqual(uintptr, uintptr): bool;
extern operator '<' macro IntPtrLessThan(intptr, intptr): bool;
extern operator '<' macro UintPtrLessThan(uintptr, uintptr): bool;
extern operator '>' macro IntPtrGreaterThan(intptr, intptr): bool;
extern operator '>' macro UintPtrGreaterThan(uintptr, uintptr): bool;
extern operator '<=' macro IntPtrLessThanOrEqual(intptr, intptr): bool;
extern operator '<=' macro UintPtrLessThanOrEqual(uintptr, uintptr): bool;
extern operator '>=' macro IntPtrGreaterThanOrEqual(intptr, intptr): bool;
extern operator '>=' macro UintPtrGreaterThanOrEqual(uintptr, uintptr): bool;
extern operator '~' macro WordNot(intptr): intptr;
extern operator '~' macro WordNot(uintptr): uintptr;
extern operator '~' macro ConstexprWordNot(constexpr intptr): constexpr intptr;
extern operator '~' macro ConstexprWordNot(constexpr uintptr):
    constexpr uintptr;

extern operator '==' macro Float64Equal(float64, float64): bool;
extern operator '!=' macro Float64NotEqual(float64, float64): bool;
extern operator '>' macro Float64GreaterThan(float64, float64): bool;
extern operator '>=' macro Float64GreaterThanOrEqual(float64, float64): bool;
extern operator '<' macro Float64LessThan(float64, float64): bool;
extern operator '<=' macro Float64LessThanOrEqual(float64, float64): bool;

extern macro BranchIfNumberEqual(Number, Number): never
    labels Taken, NotTaken;
operator '==' macro IsNumberEqual(a: Number, b: Number): bool {
  BranchIfNumberEqual(a, b) otherwise return true, return false;
}
operator '!=' macro IsNumberNotEqual(a: Number, b: Number): bool {
  return !(a == b);
}
extern macro BranchIfNumberLessThan(Number, Number): never
    labels Taken, NotTaken;
operator '<' macro NumberIsLessThan(a: Number, b: Number): bool {
  BranchIfNumberLessThan(a, b) otherwise return true, return false;
}
extern macro BranchIfNumberLessThanOrEqual(Number, Number): never
    labels Taken, NotTaken;
operator '<=' macro NumberIsLessThanOrEqual(a: Number, b: Number): bool {
  BranchIfNumberLessThanOrEqual(a, b) otherwise return true, return false;
}

operator '>' macro NumberIsGreaterThan(a: Number, b: Number): bool {
  return b < a;
}
operator '>=' macro NumberIsGreaterThanOrEqual(a: Number, b: Number): bool {
  return b <= a;
}

extern macro BranchIfFloat64IsNaN(float64): never
    labels Taken, NotTaken;
macro Float64IsNaN(n: float64): bool {
  BranchIfFloat64IsNaN(n) otherwise return true, return false;
}

// The type of all tagged values that can safely be compared with TaggedEqual.
type TaggedWithIdentity =
    JSReceiver|FixedArrayBase|Oddball|Map|WeakCell|Context|EmptyString;

extern operator '==' macro TaggedEqual(TaggedWithIdentity, Object): bool;
extern operator '==' macro TaggedEqual(Object, TaggedWithIdentity): bool;
extern operator '==' macro TaggedEqual(
    TaggedWithIdentity, TaggedWithIdentity): bool;
extern operator '==' macro TaggedEqual(WeakHeapObject, WeakHeapObject): bool;
extern operator '!=' macro TaggedNotEqual(TaggedWithIdentity, Object): bool;
extern operator '!=' macro TaggedNotEqual(Object, TaggedWithIdentity): bool;
extern operator '!=' macro TaggedNotEqual(
    TaggedWithIdentity, TaggedWithIdentity): bool;
extern operator '!=' macro TaggedNotEqual(WeakHeapObject, WeakHeapObject): bool;
// Do not overload == and != if it is unclear if object identity is the right
// equality.
extern macro TaggedEqual(MaybeObject, MaybeObject): bool;
extern macro TaggedNotEqual(MaybeObject, MaybeObject): bool;

extern operator '+' macro SmiAdd(Smi, Smi): Smi;
extern operator '-' macro SmiSub(Smi, Smi): Smi;
extern operator '&' macro SmiAnd(Smi, Smi): Smi;
extern operator '|' macro SmiOr(Smi, Smi): Smi;
extern operator '<<' macro SmiShl(Smi, constexpr int31): Smi;
extern operator '>>' macro SmiSar(Smi, constexpr int31): Smi;

extern operator '+' macro IntPtrAdd(intptr, intptr): intptr;
extern operator '+' macro ConstexprIntPtrAdd(
    constexpr intptr, constexpr intptr): constexpr intptr;
extern operator '+' macro ConstexprUintPtrAdd(
    constexpr uintptr, constexpr uintptr): constexpr intptr;
extern operator '-' macro IntPtrSub(intptr, intptr): intptr;
extern operator '*' macro IntPtrMul(intptr, intptr): intptr;
extern operator '/' macro IntPtrDiv(intptr, intptr): intptr;
extern operator '<<' macro WordShl(intptr, intptr): intptr;
extern operator '>>' macro WordSar(intptr, intptr): intptr;
extern operator '&' macro WordAnd(intptr, intptr): intptr;
extern operator '|' macro WordOr(intptr, intptr): intptr;

extern operator '+' macro UintPtrAdd(uintptr, uintptr): uintptr;
extern operator '-' macro UintPtrSub(uintptr, uintptr): uintptr;
extern operator '<<' macro WordShl(uintptr, uintptr): uintptr;
extern operator '>>>' macro WordShr(uintptr, uintptr): uintptr;
extern operator '&' macro WordAnd(uintptr, uintptr): uintptr;
extern operator '|' macro WordOr(uintptr, uintptr): uintptr;

extern operator '+' macro Int32Add(int32, int32): int32;
extern operator '+' macro ConstexprUint32Add(
    constexpr uint32, constexpr int32): constexpr uint32;
extern operator '+' macro ConstexprInt31Add(
    constexpr int31, constexpr int31): constexpr int31;
extern operator '*' macro ConstexprInt31Mul(
    constexpr int31, constexpr int31): constexpr int31;
extern operator '-' macro Int32Sub(int16, int16): int32;
extern operator '-' macro Int32Sub(uint16, uint16): int32;
extern operator '-' macro Int32Sub(int32, int32): int32;
extern operator '*' macro Int32Mul(int32, int32): int32;
extern operator '/' macro Int32Div(int32, int32): int32;
extern operator '%' macro Int32Mod(int32, int32): int32;
extern operator '&' macro Word32And(int32, int32): int32;
extern operator '&' macro Word32And(uint32, uint32): uint32;
extern operator '==' macro
ConstexprInt31Equal(constexpr int31, constexpr int31): constexpr bool;
extern operator '!=' macro
ConstexprInt31NotEqual(constexpr int31, constexpr int31): constexpr bool;
extern operator '==' macro
ConstexprUint32Equal(constexpr uint32, constexpr uint32): constexpr bool;
extern operator '!=' macro
ConstexprUint32NotEqual(constexpr uint32, constexpr uint32): constexpr bool;
extern operator '>=' macro
ConstexprInt31GreaterThanEqual(
    constexpr int31, constexpr int31): constexpr bool;
extern operator '==' macro ConstexprInt32Equal(
    constexpr int32, constexpr int32): constexpr bool;
extern operator '!=' macro ConstexprInt32NotEqual(
    constexpr int32, constexpr int32): constexpr bool;

extern operator '==' macro Word32Equal(int32, int32): bool;
extern operator '==' macro Word32Equal(uint32, uint32): bool;
extern operator '!=' macro Word32NotEqual(int32, int32): bool;
extern operator '!=' macro Word32NotEqual(uint32, uint32): bool;
extern operator '>>>' macro Word32Shr(uint32, uint32): uint32;
extern operator '>>' macro Word32Sar(int32, int32): int32;
extern operator '<<' macro Word32Shl(int32, int32): int32;
extern operator '<<' macro Word32Shl(uint32, uint32): uint32;
extern operator '|' macro Word32Or(int32, int32): int32;
extern operator '|' macro Word32Or(uint32, uint32): uint32;
extern operator '&' macro Word32And(bool, bool): bool;
extern operator '|' macro Word32Or(bool, bool): bool;
extern operator '==' macro Word32Equal(bool, bool): bool;
extern operator '!=' macro Word32NotEqual(bool, bool): bool;
extern operator '|' macro ConstexprWord32Or(
    constexpr int32, constexpr int32): constexpr int32;

extern operator '+' macro Float64Add(float64, float64): float64;
extern operator '-' macro Float64Sub(float64, float64): float64;
extern operator '*' macro Float64Mul(float64, float64): float64;
extern operator '/' macro Float64Div(float64, float64): float64;
extern operator '%' macro Float64Mod(float64, float64): float64;

extern operator '+' macro NumberAdd(Number, Number): Number;
extern operator '-' macro NumberSub(Number, Number): Number;
extern macro NumberMin(Number, Number): Number;
extern macro NumberMax(Number, Number): Number;
macro Min(x: Number, y: Number): Number {
  return NumberMin(x, y);
}
macro Max(x: Number, y: Number): Number {
  return NumberMax(x, y);
}

extern macro TryIntPtrAdd(intptr, intptr): intptr labels Overflow;
extern macro TryIntPtrSub(intptr, intptr): intptr labels Overflow;
extern macro TryInt32Mul(int32, int32): int32 labels Overflow;

extern operator '<' macro ConstexprUintPtrLessThan(
    constexpr uintptr, constexpr uintptr): constexpr bool;
extern operator '<<' macro ConstexprUintPtrShl(
    constexpr uintptr, constexpr int31): constexpr uintptr;
extern operator '>>>' macro ConstexprUintPtrShr(
    constexpr uintptr, constexpr int31): constexpr uintptr;

extern macro SmiMax(Smi, Smi): Smi;
extern macro SmiMin(Smi, Smi): Smi;
extern macro SmiMul(Smi, Smi): Number;
extern macro SmiMod(Smi, Smi): Number;

extern macro IntPtrMax(intptr, intptr): intptr;
extern macro IntPtrMin(intptr, intptr): intptr;
extern macro UintPtrMin(uintptr, uintptr): uintptr;

extern operator '!' macro ConstexprBoolNot(constexpr bool): constexpr bool;
extern operator '!' macro Word32BinaryNot(bool): bool;
extern operator '!' macro IsFalse(Boolean): bool;

extern operator '==' macro
ConstexprInt31Equal(
    constexpr InstanceType, constexpr InstanceType): constexpr bool;
extern operator '-' macro ConstexprUint32Sub(
    constexpr InstanceType, constexpr InstanceType): constexpr int32;

extern operator '.instanceType' macro LoadInstanceType(HeapObject):
    InstanceType;

operator '.length_uintptr' macro LoadJSArrayLengthAsUintPtr(array: JSArray):
    uintptr {
  return Convert<uintptr>(array.length);
}

extern operator '.length_intptr' macro LoadStringLengthAsWord(String): intptr;
operator '.length_uintptr' macro LoadStringLengthAsUintPtr(s: String): uintptr {
  return Unsigned(s.length_intptr);
}
extern operator '.length_uint32' macro LoadStringLengthAsWord32(String): uint32;
extern operator '.length_smi' macro LoadStringLengthAsSmi(String): Smi;

extern builtin StringAdd_CheckNone(implicit context: Context)(
    String, String): String;
operator '+' macro StringAdd(implicit context: Context)(
    a: String, b: String): String {
  return StringAdd_CheckNone(a, b);
}

operator '==' macro PromiseStateEquals(
    s1: PromiseState, s2: PromiseState): bool {
  return Word32Equal(s1, s2);
}

extern macro TaggedIsSmi(Object): bool;
extern macro TaggedIsNotSmi(Object): bool;
extern macro TaggedIsPositiveSmi(Object): bool;
extern macro IsValidPositiveSmi(intptr): bool;

extern macro IsInteger(JSAny): bool;
extern macro IsInteger(HeapNumber): bool;

extern macro AllocateHeapNumberWithValue(float64): HeapNumber;
extern macro ChangeInt32ToTagged(int32): Number;
extern macro ChangeUint32ToTagged(uint32): Number;
extern macro ChangeUintPtrToFloat64(uintptr): float64;
extern macro ChangeUintPtrToTagged(uintptr): Number;
extern macro Unsigned(int32): uint32;
extern macro Unsigned(int16): uint16;
extern macro Unsigned(int8): uint8;
extern macro Unsigned(intptr): uintptr;
extern macro Unsigned(RawPtr): uintptr;
extern macro Signed(uint32): int32;
extern macro Signed(uint16): int16;
extern macro Signed(uint8): int8;
extern macro Signed(uintptr): intptr;
extern macro Signed(RawPtr): intptr;
extern macro TruncateIntPtrToInt32(intptr): int32;
extern macro SmiTag(intptr): Smi;
extern macro SmiFromInt32(int32): Smi;
extern macro SmiFromUint32(uint32): Smi;
extern macro SmiFromIntPtr(intptr): Smi;
extern macro SmiUntag(Smi): intptr;
macro SmiUntag<T: type>(value: SmiTagged<T>): T {
  return %RawDownCast<T>(Unsigned(SmiToInt32(Convert<Smi>(value))));
}
macro SmiTag<T : type extends uint31>(value: T): SmiTagged<T> {
  return %RawDownCast<SmiTagged<T>>(SmiFromUint32(value));
}
extern macro SmiToInt32(Smi): int32;
extern macro SmiToFloat64(Smi): float64;
extern macro TaggedIndexToIntPtr(TaggedIndex): intptr;
extern macro IntPtrToTaggedIndex(intptr): TaggedIndex;
extern macro TaggedIndexToSmi(TaggedIndex): Smi;
extern macro SmiToTaggedIndex(Smi): TaggedIndex;
extern macro RoundIntPtrToFloat64(intptr): float64;
extern macro ChangeFloat32ToFloat64(float32): float64;
extern macro ChangeNumberToFloat64(Number): float64;
extern macro ChangeNumberToUint32(Number): uint32;
extern macro ChangeTaggedNonSmiToInt32(implicit context: Context)(JSAnyNotSmi):
    int32;
extern macro ChangeTaggedToFloat64(implicit context: Context)(JSAny): float64;
extern macro ChangeFloat64ToTagged(float64): Number;
extern macro ChangeFloat64ToUintPtr(float64): uintptr;
extern macro ChangeFloat64ToIntPtr(float64): intptr;
extern macro ChangeInt32ToFloat64(int32): float64;
extern macro ChangeInt32ToIntPtr(int32): intptr;   // Sign-extends.
extern macro ChangeUint32ToWord(uint32): uintptr;  // Doesn't sign-extend.
extern macro LoadNativeContext(Context): NativeContext;
extern macro TruncateFloat64ToFloat32(float64): float32;
extern macro TruncateHeapNumberValueToWord32(HeapNumber): int32;
extern macro LoadJSArrayElementsMap(constexpr ElementsKind, NativeContext): Map;
extern macro LoadJSArrayElementsMap(ElementsKind, NativeContext): Map;
extern macro NumberConstant(constexpr float64): Number;
extern macro NumberConstant(constexpr int32): Number;
extern macro NumberConstant(constexpr uint32): Number;
extern macro IntPtrConstant(constexpr int31): intptr;
extern macro IntPtrConstant(constexpr int32): intptr;
extern macro Uint16Constant(constexpr uint16): uint16;
extern macro Int32Constant(constexpr int31): int31;
extern macro Int32Constant(constexpr int32): int32;
extern macro Float64Constant(constexpr int31): float64;
extern macro Float64Constant(constexpr float64): float64;
extern macro SmiConstant(constexpr int31): Smi;
extern macro SmiConstant(constexpr Smi): Smi;
extern macro SmiConstant(constexpr MessageTemplate): Smi;
extern macro SmiConstant(constexpr bool): Smi;
extern macro SmiConstant(constexpr uint32): Smi;
extern macro BoolConstant(constexpr bool): bool;
extern macro StringConstant(constexpr string): String;
extern macro IntPtrConstant(constexpr ContextSlot): ContextSlot;
extern macro IntPtrConstant(constexpr intptr): intptr;
extern macro PointerConstant(constexpr RawPtr): RawPtr;
extern macro SingleCharacterStringConstant(constexpr string): String;
extern macro Float64SilenceNaN(float64): float64;

extern macro BitcastWordToTaggedSigned(intptr): Smi;
extern macro BitcastWordToTaggedSigned(uintptr): Smi;
extern macro BitcastWordToTagged(intptr): Object;
extern macro BitcastWordToTagged(uintptr): Object;
extern macro BitcastTaggedToWord(Tagged): intptr;
extern macro BitcastTaggedToWordForTagAndSmiBits(Tagged): intptr;

extern macro FixedArrayMapConstant(): Map;
extern macro FixedDoubleArrayMapConstant(): Map;
extern macro FixedCOWArrayMapConstant(): Map;
extern macro EmptyByteArrayConstant(): ByteArray;
extern macro EmptyFixedArrayConstant(): EmptyFixedArray;
extern macro PromiseCapabilityMapConstant(): Map;
extern macro OneByteStringMapConstant(): Map;
extern macro StringMapConstant(): Map;

const kFixedArrayMap: Map = FixedArrayMapConstant();
const kFixedDoubleArrayMap: Map = FixedDoubleArrayMapConstant();
const kCOWMap: Map = FixedCOWArrayMapConstant();
const kEmptyByteArray: ByteArray = EmptyByteArrayConstant();
const kEmptyFixedArray: EmptyFixedArray = EmptyFixedArrayConstant();
const kPromiseCapabilityMap: Map = PromiseCapabilityMapConstant();
// The map of a non-internalized internal SeqOneByteString.
const kOneByteStringMap: Map = OneByteStringMapConstant();
// The map of a non-internalized internal SeqTwoByteString.
const kStringMap: Map = StringMapConstant();

macro OutOfBounds<T: type, X: type>(index: T, length: X): bool {
  return UintPtrGreaterThanOrEqual(
      Convert<uintptr>(Convert<intptr>(index)),
      Convert<uintptr>(Convert<intptr>(length)));
}

extern macro IsPrototypeInitialArrayPrototype(implicit context: Context)(Map):
    bool;
extern macro IsNoElementsProtectorCellInvalid(): bool;
extern macro IsArrayIteratorProtectorCellInvalid(): bool;
extern macro IsArraySpeciesProtectorCellInvalid(): bool;
extern macro IsTypedArraySpeciesProtectorCellInvalid(): bool;
extern macro IsPromiseSpeciesProtectorCellInvalid(): bool;
extern macro IsMockArrayBufferAllocatorFlag(): bool;
extern macro HasBuiltinSubclassingFlag(): bool;
extern macro IsPrototypeTypedArrayPrototype(implicit context: Context)(Map):
    bool;

extern operator '.data_ptr' macro LoadJSTypedArrayDataPtr(JSTypedArray): RawPtr;

extern operator '.elements_kind' macro LoadMapElementsKind(Map): ElementsKind;
extern operator '.elements_kind' macro LoadElementsKind(JSTypedArray):
    ElementsKind;

extern operator '.length' macro LoadFastJSArrayLength(FastJSArray): Smi;
operator '.length=' macro StoreFastJSArrayLength(
    array: FastJSArray, length: Smi) {
  const array: JSArray = array;
  array.length = length;
}

extern macro GetNumberDictionaryNumberOfElements(NumberDictionary): Smi;

extern macro LoadConstructorOrBackPointer(Map): Object;

extern macro BasicLoadNumberDictionaryElement(NumberDictionary, intptr): JSAny
    labels NotData, IfHole;

extern macro IsFastElementsKind(ElementsKind): bool;
extern macro IsDoubleElementsKind(ElementsKind): bool;
extern macro IsFastSmiOrTaggedElementsKind(ElementsKind): bool;
extern macro IsFastSmiElementsKind(ElementsKind): bool;
extern macro IsHoleyFastElementsKind(ElementsKind): bool;

macro FastHoleyElementsKind(kind: ElementsKind): ElementsKind {
  if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
    return ElementsKind::HOLEY_SMI_ELEMENTS;
  } else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
    return ElementsKind::HOLEY_DOUBLE_ELEMENTS;
  }
  assert(kind == ElementsKind::PACKED_ELEMENTS);
  return ElementsKind::HOLEY_ELEMENTS;
}

macro AllowDoubleElements(kind: ElementsKind): ElementsKind {
  if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
    return ElementsKind::PACKED_DOUBLE_ELEMENTS;
  } else if (kind == ElementsKind::HOLEY_SMI_ELEMENTS) {
    return ElementsKind::HOLEY_DOUBLE_ELEMENTS;
  }
  return kind;
}

macro AllowNonNumberElements(kind: ElementsKind): ElementsKind {
  if (kind == ElementsKind::PACKED_SMI_ELEMENTS) {
    return ElementsKind::PACKED_ELEMENTS;
  } else if (kind == ElementsKind::HOLEY_SMI_ELEMENTS) {
    return ElementsKind::HOLEY_ELEMENTS;
  } else if (kind == ElementsKind::PACKED_DOUBLE_ELEMENTS) {
    return ElementsKind::PACKED_ELEMENTS;
  } else if (kind == ElementsKind::HOLEY_DOUBLE_ELEMENTS) {
    return ElementsKind::HOLEY_ELEMENTS;
  }
  return kind;
}

macro GetObjectFunction(implicit context: Context)(): JSFunction {
  return *NativeContextSlot(ContextSlot::OBJECT_FUNCTION_INDEX);
}
macro GetArrayFunction(implicit context: Context)(): JSFunction {
  return *NativeContextSlot(ContextSlot::ARRAY_FUNCTION_INDEX);
}
macro GetArrayBufferFunction(implicit context: Context)(): Constructor {
  return *NativeContextSlot(ContextSlot::ARRAY_BUFFER_FUN_INDEX);
}
macro GetArrayBufferNoInitFunction(implicit context: Context)(): JSFunction {
  return *NativeContextSlot(ContextSlot::ARRAY_BUFFER_NOINIT_FUN_INDEX);
}
macro GetFastPackedElementsJSArrayMap(implicit context: Context)(): Map {
  return *NativeContextSlot(ContextSlot::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX);
}
macro GetFastPackedSmiElementsJSArrayMap(implicit context: Context)(): Map {
  return *NativeContextSlot(
      ContextSlot::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX);
}
macro GetProxyRevocableResultMap(implicit context: Context)(): Map {
  return *NativeContextSlot(ContextSlot::PROXY_REVOCABLE_RESULT_MAP_INDEX);
}
macro GetIteratorResultMap(implicit context: Context)(): Map {
  return *NativeContextSlot(ContextSlot::ITERATOR_RESULT_MAP_INDEX);
}
macro GetInitialStringIteratorMap(implicit context: Context)(): Map {
  return *NativeContextSlot(ContextSlot::INITIAL_STRING_ITERATOR_MAP_INDEX);
}
macro GetReflectApply(implicit context: Context)(): Callable {
  return *NativeContextSlot(ContextSlot::REFLECT_APPLY_INDEX);
}
macro GetRegExpLastMatchInfo(implicit context: Context)(): RegExpMatchInfo {
  return *NativeContextSlot(ContextSlot::REGEXP_LAST_MATCH_INFO_INDEX);
}
macro GetStrictArgumentsMap(implicit context: Context)(): Map {
  return *NativeContextSlot(ContextSlot::STRICT_ARGUMENTS_MAP_INDEX);
}
macro GetSloppyArgumentsMap(implicit context: Context)(): Map {
  return *NativeContextSlot(ContextSlot::SLOPPY_ARGUMENTS_MAP_INDEX);
}
macro GetFastAliasedArgumentsMap(implicit context: Context)(): Map {
  return *NativeContextSlot(ContextSlot::FAST_ALIASED_ARGUMENTS_MAP_INDEX);
}
macro GetWeakCellMap(implicit context: Context)(): Map {
  return %GetClassMapConstant<WeakCell>();
}

// Call(Context, Target, Receiver, ...Args)
// TODO(joshualitt): Assuming the context parameter is for throwing when Target
//                   is non-callable, then we should make it an implicit
//                   parameter.
extern transitioning macro Call(Context, JSAny, JSAny): JSAny;
extern transitioning macro Call(Context, JSAny, JSAny, JSAny): JSAny;
extern transitioning macro Call(Context, JSAny, JSAny, JSAny, JSAny): JSAny;
extern transitioning macro Call(
    Context, JSAny, JSAny, JSAny, JSAny, JSAny): JSAny;
extern transitioning macro Call(
    Context, JSAny, JSAny, JSAny, JSAny, JSAny, JSAny): JSAny;
extern transitioning macro Call(
    Context, JSAny, JSAny, JSAny, JSAny, JSAny, JSAny, JSAny): JSAny;

extern macro TransitionElementsKind(
    JSObject, Map, constexpr ElementsKind,
    constexpr ElementsKind): void labels Bailout;
extern macro PerformStackCheck(implicit context: Context)(): void;

extern macro Typeof(JSAny): String;

// Return true iff number is NaN.
macro NumberIsNaN(number: Number): bool {
  typeswitch (number) {
    case (Smi): {
      return false;
    }
    case (hn: HeapNumber): {
      const value: float64 = Convert<float64>(hn);
      return value != value;
    }
  }
}

extern macro GotoIfForceSlowPath() labels Taken;
macro IsForceSlowPath(): bool {
  GotoIfForceSlowPath() otherwise return true;
  return false;
}

extern macro BranchIfToBooleanIsTrue(JSAny): never
    labels Taken, NotTaken;
extern macro BranchIfToBooleanIsFalse(JSAny): never
    labels Taken, NotTaken;

macro ToBoolean(obj: JSAny): bool {
  BranchIfToBooleanIsTrue(obj) otherwise return true, return false;
}

@export
macro RequireObjectCoercible(implicit context: Context)(
    value: JSAny, name: constexpr string): JSAny {
  if (IsNullOrUndefined(value)) {
    ThrowTypeError(MessageTemplate::kCalledOnNullOrUndefined, name);
  }
  return value;
}

extern macro BranchIfSameValue(JSAny, JSAny): never labels Taken, NotTaken;
macro SameValue(a: JSAny, b: JSAny): bool {
  BranchIfSameValue(a, b) otherwise return true, return false;
}

// Does "if (index1 + index2 > limit) goto IfOverflow" in an uintptr overflow
// friendly way where index1 and index2 are in [0, kMaxSafeInteger] range.
macro CheckIntegerIndexAdditionOverflow(
    index1: uintptr, index2: uintptr, limit: uintptr) labels IfOverflow {
  if constexpr (Is64()) {
    assert(index1 <= kMaxSafeIntegerUint64);
    assert(index2 <= kMaxSafeIntegerUint64);
    // Given that both index1 and index2 are in a safe integer range the
    // addition can't overflow.
    if (index1 + index2 > limit) goto IfOverflow;
  } else {
    // Uintptr range is "smaller" than [0, kMaxSafeInteger] range, so
    // "index1 + index2" may overflow, so we check the condition in the
    // following way "if (index1 > limit - index2) goto IfOverflow" and check
    // that "limit - index2" does not underflow.
    const index1Limit = limit - index2;
    if (index1 > index1Limit) goto IfOverflow;
    // Handle potential index1Limit underflow.
    if (index1Limit > limit) goto IfOverflow;
  }
}

// TODO(turbofan): Define enum here once they appear in Torque.
//
// The value is a SafeInteger that fits into uintptr range, so no bounds checks
// are necessary.
const kModeValueIsSafeIntegerUintPtr: constexpr int31 = 0;
// The value is a SafeInteger that may not fit into uintptr range, so only
// uintptr bounds check is necessary.
const kModeValueIsSafeInteger: constexpr int31 = 1;
// The value is can be whatever non-NaN number, all checks are necessary.
const kModeValueIsAnyNumber: constexpr int31 = 2;

macro TryNumberToUintPtr(valueNumber: Number, kMode: constexpr int31):
    uintptr labels IfLessThanZero, IfUIntPtrOverflow, IfSafeIntegerOverflow {
  typeswitch (valueNumber) {
    case (valueSmi: Smi): {
      if (kMode == kModeValueIsAnyNumber) {
        if (valueSmi < 0) goto IfLessThanZero;
      } else {
        assert(valueSmi >= 0);
      }
      const value: uintptr = Unsigned(Convert<intptr>(valueSmi));
      // Positive Smi values definitely fit into both [0, kMaxSafeInteger] and
      // [0, kMaxUintPtr] ranges.
      return value;
    }
    case (valueHeapNumber: HeapNumber): {
      assert(IsNumberNormalized(valueHeapNumber));
      const valueDouble: float64 = Convert<float64>(valueHeapNumber);
      // NaNs must be handled outside.
      assert(!Float64IsNaN(valueDouble));
      if (kMode == kModeValueIsAnyNumber) {
        if (valueDouble < 0) goto IfLessThanZero;
      } else {
        assert(valueDouble >= 0);
      }

      if constexpr (Is64()) {
        // On 64-bit architectures uintptr range is bigger than safe integer
        // range.
        if (kMode == kModeValueIsAnyNumber) {
          if (valueDouble > kMaxSafeInteger) goto IfSafeIntegerOverflow;
        } else {
          assert(valueDouble <= kMaxSafeInteger);
        }
      } else {
        // On 32-bit architectures uintptr range is smaller than safe integer
        // range.
        if (kMode == kModeValueIsAnyNumber ||
            kMode == kModeValueIsSafeInteger) {
          if (valueDouble > kMaxUInt32Double) goto IfUIntPtrOverflow;
        } else {
          assert(valueDouble <= kMaxUInt32Double);
        }
      }
      return ChangeFloat64ToUintPtr(valueDouble);
    }
  }
}

@export
macro ChangeUintPtrNumberToUintPtr(value: Number): uintptr {
  try {
    return TryNumberToUintPtr(value, kModeValueIsSafeIntegerUintPtr)
        otherwise InvalidValue, InvalidValue, InvalidValue;
  } label InvalidValue {
    unreachable;
  }
}

@export
macro ChangeSafeIntegerNumberToUintPtr(value: Number):
    uintptr labels IfUIntPtrOverflow {
  try {
    return TryNumberToUintPtr(value, kModeValueIsSafeInteger)
        otherwise InvalidValue, IfUIntPtrOverflow, InvalidValue;
  } label InvalidValue {
    unreachable;
  }
}

transitioning macro ToUintPtr(implicit context: Context)(value: JSAny):
    uintptr labels IfLessThanZero, IfUIntPtrOverflow, IfSafeIntegerOverflow {
  if (value == Undefined) return 0;
  const indexNumber = ToInteger_Inline(value);
  return TryNumberToUintPtr(indexNumber, kModeValueIsAnyNumber)
      otherwise IfLessThanZero, IfUIntPtrOverflow, IfSafeIntegerOverflow;
}

// https://tc39.github.io/ecma262/#sec-toindex
// Unlike ToIndex from the spec this implementation triggers IfRangeError if
// the result is bigger than min(kMaxUIntPtr, kMaxSafeInteger).
// We can do this because all callers do a range checks against uintptr length
// anyway and throw a RangeError in case of out-of-bounds index.
@export
transitioning macro ToIndex(implicit context: Context)(value: JSAny):
    uintptr labels IfRangeError {
  if (value == Undefined) return 0;
  const indexNumber = ToInteger_Inline(value);
  // Less than 0 case, uintptr range overflow and safe integer range overflow
  // imply IfRangeError.
  return TryNumberToUintPtr(indexNumber, kModeValueIsAnyNumber)
      otherwise IfRangeError, IfRangeError, IfRangeError;
}

transitioning macro GetLengthProperty(implicit context: Context)(o: JSAny):
    Number {
  try {
    typeswitch (o) {
      case (a: JSArray): {
        return a.length;
      }
      case (a: JSStrictArgumentsObject): {
        goto ToLength(a.length);
      }
      case (a: JSSloppyArgumentsObject): {
        goto ToLength(a.length);
      }
      case (JSAny): deferred {
        goto ToLength(GetProperty(o, kLengthString));
      }
    }
  } label ToLength(length: JSAny) deferred {
    return ToLength_Inline(length);
  }
}

transitioning macro GetMethod(implicit context: Context)(
    o: JSAny, name: AnyName): Callable labels IfNullOrUndefined,
    IfMethodNotCallable(JSAny) {
  const value = GetProperty(o, name);
  // TODO(v8:9933): Consider checking for null/undefined after checking for
  // callable because the latter seems to be more common.
  if (value == Undefined || value == Null) goto IfNullOrUndefined;
  return Cast<Callable>(value)
      otherwise goto IfMethodNotCallable(value);
}

transitioning macro GetMethod(implicit context: Context)(
    o: JSAny, name: String): Callable labels IfNullOrUndefined {
  try {
    return GetMethod(o, name) otherwise IfNullOrUndefined, IfMethodNotCallable;
  } label IfMethodNotCallable(value: JSAny) deferred {
    ThrowTypeError(MessageTemplate::kPropertyNotFunction, value, name, o);
  }
}

transitioning macro GetMethod(implicit context: Context)(
    o: JSAny, name: constexpr string): Callable labels IfNullOrUndefined {
  return GetMethod(o, StringConstant(name)) otherwise IfNullOrUndefined;
}

transitioning macro GetMethod(implicit context: Context)(
    o: JSAny, symbol: Symbol): Callable labels IfNullOrUndefined {
  const value = GetProperty(o, symbol);
  if (value == Undefined || value == Null) goto IfNullOrUndefined;
  return Cast<Callable>(value)
      otherwise ThrowTypeError(
      MessageTemplate::kPropertyNotFunction, value, symbol, o);
}

extern macro IsOneByteStringInstanceType(InstanceType): bool;

// After converting an index to an integer, calculate a relative index:
// return index < 0 ? max(length + index, 0) : min(index, length)
@export
transitioning macro ConvertToRelativeIndex(implicit context: Context)(
    index: JSAny, length: uintptr): uintptr {
  const indexNumber: Number = ToInteger_Inline(index);
  return ConvertToRelativeIndex(indexNumber, length);
}

// Calculate a relative index:
// return index < 0 ? max(length + index, 0) : min(index, length)
@export
macro ConvertToRelativeIndex(indexNumber: Number, length: uintptr): uintptr {
  typeswitch (indexNumber) {
    case (indexSmi: Smi): {
      const indexIntPtr: intptr = Convert<intptr>(indexSmi);
      // The logic is implemented using unsigned types.
      if (indexIntPtr < 0) {
        const relativeIndex: uintptr = Unsigned(indexIntPtr) + length;
        return relativeIndex < length ? relativeIndex : 0;

      } else {
        const relativeIndex: uintptr = Unsigned(indexIntPtr);
        return relativeIndex < length ? relativeIndex : length;
      }
    }
    case (indexHeapNumber: HeapNumber): {
      assert(IsNumberNormalized(indexHeapNumber));
      const indexDouble: float64 = Convert<float64>(indexHeapNumber);
      // NaNs must already be handled by ConvertToRelativeIndex() version
      // above accepting JSAny indices.
      assert(!Float64IsNaN(indexDouble));
      const lengthDouble: float64 = Convert<float64>(length);
      assert(lengthDouble <= kMaxSafeInteger);
      if (indexDouble < 0) {
        const relativeIndex: float64 = lengthDouble + indexDouble;
        return relativeIndex > 0 ? ChangeFloat64ToUintPtr(relativeIndex) : 0;

      } else {
        return ChangeFloat64ToUintPtr(
            indexDouble < lengthDouble ? indexDouble : lengthDouble);
      }
    }
  }
}

// After converting an index to a signed integer, clamps it to the provided
// range [0, limit]:
// return min(max(index, 0), limit)
@export
transitioning macro ClampToIndexRange(implicit context: Context)(
    index: JSAny, limit: uintptr): uintptr {
  const indexNumber: Number = ToInteger_Inline(index);
  return ClampToIndexRange(indexNumber, limit);
}

// Clamps given signed indexNumber to the provided range [0, limit]:
// return min(max(index, 0), limit)
@export
macro ClampToIndexRange(indexNumber: Number, limit: uintptr): uintptr {
  typeswitch (indexNumber) {
    case (indexSmi: Smi): {
      if (indexSmi < 0) return 0;
      const index: uintptr = Unsigned(Convert<intptr>(indexSmi));
      if (index >= limit) return limit;
      return index;
    }
    case (indexHeapNumber: HeapNumber): {
      assert(IsNumberNormalized(indexHeapNumber));
      const indexDouble: float64 = Convert<float64>(indexHeapNumber);
      // NaNs must already be handled by ClampToIndexRange() version
      // above accepting JSAny indices.
      assert(!Float64IsNaN(indexDouble));
      if (indexDouble <= 0) return 0;

      const maxIndexDouble: float64 = Convert<float64>(limit);
      assert(maxIndexDouble <= kMaxSafeInteger);
      if (indexDouble >= maxIndexDouble) return limit;

      return ChangeFloat64ToUintPtr(indexDouble);
    }
  }
}

extern builtin ObjectToString(Context, JSAny): String;
extern builtin StringRepeat(Context, String, Number): String;

@export
struct KeyValuePair {
  key: JSAny;
  value: JSAny;
}

// Macro definitions for compatibility that expose functionality to the CSA
// using "legacy" APIs. In Torque code, these should not be used.
@export
macro IsFastJSArray(o: Object, context: Context): bool {
  // Long-term, it's likely not a good idea to have this slow-path test here,
  // since it fundamentally breaks the type system.
  if (IsForceSlowPath()) return false;
  return Is<FastJSArray>(o);
}

@export
macro BranchIfFastJSArray(o: Object, context: Context): never labels True,
    False {
  if (IsFastJSArray(o, context)) {
    goto True;
  } else {
    goto False;
  }
}

@export
macro BranchIfFastJSArrayForRead(o: Object, context: Context):
    never labels True, False {
  // Long-term, it's likely not a good idea to have this slow-path test here,
  // since it fundamentally breaks the type system.
  if (IsForceSlowPath()) goto False;
  if (Is<FastJSArrayForRead>(o)) {
    goto True;
  } else {
    goto False;
  }
}

@export
macro IsFastJSArrayWithNoCustomIteration(context: Context, o: Object): bool {
  return Is<FastJSArrayWithNoCustomIteration>(o);
}

@export
macro IsFastJSArrayForReadWithNoCustomIteration(context: Context, o: Object):
    bool {
  return Is<FastJSArrayForReadWithNoCustomIteration>(o);
}

extern transitioning runtime
CreateDataProperty(implicit context: Context)(JSReceiver, JSAny, JSAny);

extern transitioning runtime SetOwnPropertyIgnoreAttributes(
    implicit context: Context)(JSObject, String, JSAny, Smi);

namespace runtime {
extern runtime
GetDerivedMap(Context, JSFunction, JSReceiver): Map;
}
extern macro IsDeprecatedMap(Map): bool;

transitioning builtin FastCreateDataProperty(implicit context: Context)(
    receiver: JSReceiver, key: JSAny, value: JSAny): Object {
  try {
    const array = Cast<FastJSArray>(receiver) otherwise Slow;
    const index: Smi = Cast<Smi>(key) otherwise goto Slow;
    if (index < 0 || index > array.length) goto Slow;
    const isAppend = index == array.length;

    if (isAppend) {
      // Fast append only works on fast elements kind and with writable length.
      const kind = EnsureArrayPushable(array.map) otherwise Slow;
      array::EnsureWriteableFastElements(array);

      // We may have to transition a.
      // For now, if transition is required, jump away to slow.
      if (IsFastSmiElementsKind(kind)) {
        BuildAppendJSArray(ElementsKind::HOLEY_SMI_ELEMENTS, array, value)
            otherwise Slow;
      } else if (IsDoubleElementsKind(kind)) {
        BuildAppendJSArray(ElementsKind::HOLEY_DOUBLE_ELEMENTS, array, value)
            otherwise Slow;
      } else {
        assert(IsFastSmiOrTaggedElementsKind(kind));
        BuildAppendJSArray(ElementsKind::HOLEY_ELEMENTS, array, value)
            otherwise Slow;
      }
    } else {
      // Non-appending element store.
      const kind = array.map.elements_kind;
      array::EnsureWriteableFastElements(array);

      // We may have to transition a.
      // For now, if transition is required, jump away to slow.
      if (IsFastSmiElementsKind(kind)) {
        const smiValue = Cast<Smi>(value) otherwise Slow;
        const elements = Cast<FixedArray>(array.elements) otherwise unreachable;
        elements[index] = smiValue;
      } else if (IsDoubleElementsKind(kind)) {
        const numberValue = Cast<Number>(value) otherwise Slow;
        const doubleElements = Cast<FixedDoubleArray>(array.elements)
            otherwise unreachable;
        doubleElements[index] = numberValue;
      } else {
        assert(IsFastSmiOrTaggedElementsKind(kind));
        const elements = Cast<FixedArray>(array.elements) otherwise unreachable;
        elements[index] = value;
      }
    }
  } label Slow {
    CreateDataProperty(receiver, key, value);
  }
  return Undefined;
}

macro VerifiedUnreachable(): never {
  static_assert(false);
  unreachable;
}

macro Float64IsSomeInfinity(value: float64): bool {
  if (value == V8_INFINITY) {
    return true;
  }
  return value == (Convert<float64>(0) - V8_INFINITY);
}

@export
macro IsIntegerOrSomeInfinity(o: Object): bool {
  typeswitch (o) {
    case (Smi): {
      return true;
    }
    case (hn: HeapNumber): {
      if (Float64IsSomeInfinity(Convert<float64>(hn))) {
        return true;
      }
      return IsInteger(hn);
    }
    case (Object): {
      return false;
    }
  }
}

builtin CheckNumberInRange(implicit context: Context)(
    value: Number, min: Number, max: Number, nodeId: Smi): Undefined {
  if (IsIntegerOrSomeInfinity(value) && min <= value && value <= max) {
    return Undefined;
  } else {
    Print('Range type assertion failed! (value/min/max/nodeId)');
    Print(value);
    Print(min);
    Print(max);
    Print(nodeId);
    unreachable;
  }
}

// Assert that the objects satisfy SameValue or are both the hole.
builtin CheckSameObject(implicit context: Context)(
    lhs: Object, rhs: Object): Undefined {
  typeswitch (lhs) {
    case (TheHole): {
      if (rhs == TheHole) return Undefined;
    }
    case (a: JSAny): {
      typeswitch (rhs) {
        case (b: JSAny): {
          if (SameValue(a, b)) return Undefined;
        }
        case (Object): {
        }
      }
    }
    case (Object): {
    }
  }
  Print('Distinct or unexpected values in CheckSameObject');
  unreachable;
}

macro ReplaceTheHoleWithUndefined(o: JSAny|TheHole): JSAny {
  typeswitch (o) {
    case (TheHole): {
      return Undefined;
    }
    case (a: JSAny): {
      return a;
    }
  }
}

extern macro DecodeScopeInfoHasContextExtension(intptr): intptr;

struct ConstantIterator<T: type> {
  macro Empty(): bool {
    return false;
  }
  macro Next(): T labels _NoMore {
    return this.value;
  }

  value: T;
}
macro ConstantIterator<T: type>(value: T): ConstantIterator<T> {
  return ConstantIterator{value};
}

extern macro FeedbackIteratorEntrySize(): intptr;
extern macro FeedbackIteratorHandlerOffset(): intptr;
extern operator '[]' macro LoadWeakFixedArrayElement(
    WeakFixedArray, intptr): MaybeObject;