// 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. extern macro IsBigInt(HeapObject): bool; extern macro IsConstructor(HeapObject): bool; extern macro IsCustomElementsReceiverInstanceType(int32): bool; extern macro IsExtensibleMap(Map): bool; extern macro IsNumberNormalized(Number): bool; extern macro IsSafeInteger(Object): bool; @export macro IsAccessorInfo(o: HeapObject): bool { return Is<AccessorInfo>(o); } @export macro IsAccessorPair(o: HeapObject): bool { return Is<AccessorPair>(o); } @export macro IsAllocationSite(o: HeapObject): bool { return Is<AllocationSite>(o); } @export macro IsCell(o: HeapObject): bool { return Is<Cell>(o); } @export macro IsCode(o: HeapObject): bool { return Is<Code>(o); } @export macro IsCodeDataContainer(o: HeapObject): bool { return Is<CodeDataContainer>(o); } @export macro IsContext(o: HeapObject): bool { return Is<Context>(o); } @export macro IsCoverageInfo(o: HeapObject): bool { return Is<CoverageInfo>(o); } @export macro IsDebugInfo(o: HeapObject): bool { return Is<DebugInfo>(o); } @export macro IsFixedDoubleArray(o: HeapObject): bool { return Is<FixedDoubleArray>(o); } @export macro IsFeedbackCell(o: HeapObject): bool { return Is<FeedbackCell>(o); } @export macro IsFeedbackVector(o: HeapObject): bool { return Is<FeedbackVector>(o); } @export macro IsHeapNumber(o: HeapObject): bool { return Is<HeapNumber>(o); } @export macro IsNativeContext(o: HeapObject): bool { return Is<NativeContext>(o); } @export macro IsNumber(o: Object): bool { return Is<Number>(o); } @export macro IsPrivateSymbol(o: HeapObject): bool { return Is<PrivateSymbol>(o); } @export macro IsPromiseCapability(o: HeapObject): bool { return Is<PromiseCapability>(o); } @export macro IsPromiseFulfillReactionJobTask(o: HeapObject): bool { return Is<PromiseFulfillReactionJobTask>(o); } @export macro IsPromiseReaction(o: HeapObject): bool { return Is<PromiseReaction>(o); } @export macro IsPromiseRejectReactionJobTask(o: HeapObject): bool { return Is<PromiseRejectReactionJobTask>(o); } @export macro IsSharedFunctionInfo(o: HeapObject): bool { return Is<SharedFunctionInfo>(o); } @export macro IsSymbol(o: HeapObject): bool { return Is<Symbol>(o); } extern macro TaggedToHeapObject(Object): HeapObject labels CastError; extern macro TaggedToSmi(Object): Smi labels CastError; extern macro TaggedToPositiveSmi(Object): PositiveSmi labels CastError; extern macro TaggedToDirectString(Object): DirectString labels CastError; extern macro HeapObjectToCallable(HeapObject): Callable labels CastError; extern macro HeapObjectToConstructor(HeapObject): Constructor labels CastError; extern macro HeapObjectToJSFunctionWithPrototypeSlot(HeapObject): JSFunctionWithPrototypeSlot labels CastError; macro Cast<A : type extends WeakHeapObject>(o: A|Object): A labels CastError { if (!IsWeakOrCleared(o)) goto CastError; return %RawDownCast<A>(o); } macro Cast<A : type extends Object>(implicit context: Context)(o: MaybeObject): A labels CastError { typeswitch (o) { case (WeakHeapObject): { goto CastError; } case (o: Object): { return Cast<A>(o) otherwise CastError; } } } Cast<Undefined>(o: MaybeObject): Undefined labels CastError { if (TaggedNotEqual(o, Undefined)) goto CastError; return %RawDownCast<Undefined>(o); } macro Cast<A : type extends Object>(implicit context: Context)(o: Object): A labels CastError { return Cast<A>(TaggedToHeapObject(o) otherwise CastError) otherwise CastError; } // This is required for casting MaybeObject to Object. Cast<Smi>(o: Object): Smi labels CastError { return TaggedToSmi(o) otherwise CastError; } Cast<PositiveSmi>(o: Object): PositiveSmi labels CastError { return TaggedToPositiveSmi(o) otherwise CastError; } Cast<Zero>(o: Object): Zero labels CastError { if (TaggedEqual(o, SmiConstant(0))) return %RawDownCast<Zero>(o); goto CastError; } Cast<Number>(o: Object): Number labels CastError { typeswitch (o) { case (s: Smi): { return s; } case (n: HeapNumber): { return n; } case (Object): { goto CastError; } } } Cast<Undefined>(o: Object): Undefined labels CastError { const o: MaybeObject = o; return Cast<Undefined>(o) otherwise CastError; } Cast<Numeric>(o: Object): Numeric labels CastError { typeswitch (o) { case (o: Number): { return o; } case (o: BigInt): { return o; } case (HeapObject): { goto CastError; } } } Cast<TheHole>(o: Object): TheHole labels CastError { if (o == TheHole) return %RawDownCast<TheHole>(o); goto CastError; } Cast<TheHole>(o: HeapObject): TheHole labels CastError { const o: Object = o; return Cast<TheHole>(o) otherwise CastError; } Cast<True>(o: Object): True labels CastError { if (o == True) return %RawDownCast<True>(o); goto CastError; } Cast<True>(o: HeapObject): True labels CastError { const o: Object = o; return Cast<True>(o) otherwise CastError; } Cast<False>(o: Object): False labels CastError { if (o == False) return %RawDownCast<False>(o); goto CastError; } Cast<False>(o: HeapObject): False labels CastError { const o: Object = o; return Cast<False>(o) otherwise CastError; } Cast<Boolean>(o: Object): Boolean labels CastError { typeswitch (o) { case (o: True): { return o; } case (o: False): { return o; } case (Object): { goto CastError; } } } Cast<Boolean>(o: HeapObject): Boolean labels CastError { const o: Object = o; return Cast<Boolean>(o) otherwise CastError; } // TODO(turbofan): These trivial casts for union types should be generated // automatically. Cast<JSPrimitive>(o: Object): JSPrimitive labels CastError { typeswitch (o) { case (o: Numeric): { return o; } case (o: String): { return o; } case (o: Symbol): { return o; } case (o: Boolean): { return o; } case (o: Undefined): { return o; } case (o: Null): { return o; } case (Object): { goto CastError; } } } Cast<JSAny>(o: Object): JSAny labels CastError { typeswitch (o) { case (o: JSPrimitive): { return o; } case (o: JSReceiver): { return o; } case (Object): { goto CastError; } } } Cast<JSAny|TheHole>(o: Object): JSAny|TheHole labels CastError { typeswitch (o) { case (o: JSAny): { return o; } case (o: TheHole): { return o; } case (Object): { goto CastError; } } } Cast<Number|TheHole>(o: Object): Number|TheHole labels CastError { typeswitch (o) { case (o: Number): { return o; } case (o: TheHole): { return o; } case (Object): { goto CastError; } } } Cast<Context|Zero|Undefined>(o: Object): Context|Zero|Undefined labels CastError { typeswitch (o) { case (o: Context): { return o; } case (o: Zero): { return o; } case (o: Undefined): { return o; } case (Object): { goto CastError; } } } macro Cast<A : type extends HeapObject>(o: HeapObject): A labels CastError; Cast<HeapObject>(o: HeapObject): HeapObject labels _CastError { return o; } Cast<Null>(o: HeapObject): Null labels CastError { if (o != Null) goto CastError; return %RawDownCast<Null>(o); } Cast<Undefined>(o: HeapObject): Undefined labels CastError { const o: MaybeObject = o; return Cast<Undefined>(o) otherwise CastError; } Cast<EmptyFixedArray>(o: Object): EmptyFixedArray labels CastError { if (o != kEmptyFixedArray) goto CastError; return %RawDownCast<EmptyFixedArray>(o); } Cast<EmptyFixedArray>(o: HeapObject): EmptyFixedArray labels CastError { const o: Object = o; return Cast<EmptyFixedArray>(o) otherwise CastError; } Cast<(FixedDoubleArray | EmptyFixedArray)>(o: HeapObject): FixedDoubleArray| EmptyFixedArray labels CastError { typeswitch (o) { case (o: EmptyFixedArray): { return o; } case (o: FixedDoubleArray): { return o; } case (HeapObject): { goto CastError; } } } Cast<Callable>(o: HeapObject): Callable labels CastError { return HeapObjectToCallable(o) otherwise CastError; } Cast<Undefined|Callable>(o: HeapObject): Undefined|Callable labels CastError { if (o == Undefined) return Undefined; return HeapObjectToCallable(o) otherwise CastError; } Cast<Undefined|JSFunction>(o: HeapObject): Undefined|JSFunction labels CastError { if (o == Undefined) return Undefined; return Cast<JSFunction>(o) otherwise CastError; } macro Cast<T : type extends Symbol>(o: Symbol): T labels CastError; Cast<PublicSymbol>(s: Symbol): PublicSymbol labels CastError { if (s.flags.is_private) goto CastError; return %RawDownCast<PublicSymbol>(s); } Cast<PrivateSymbol>(s: Symbol): PrivateSymbol labels CastError { if (s.flags.is_private) return %RawDownCast<PrivateSymbol>(s); goto CastError; } Cast<PublicSymbol>(o: HeapObject): PublicSymbol labels CastError { const s = Cast<Symbol>(o) otherwise CastError; return Cast<PublicSymbol>(s) otherwise CastError; } Cast<PrivateSymbol>(o: HeapObject): PrivateSymbol labels CastError { const s = Cast<Symbol>(o) otherwise CastError; return Cast<PrivateSymbol>(s) otherwise CastError; } Cast<DirectString>(o: String): DirectString labels CastError { return TaggedToDirectString(o) otherwise CastError; } Cast<Constructor>(o: HeapObject): Constructor labels CastError { return HeapObjectToConstructor(o) otherwise CastError; } Cast<JSFunctionWithPrototypeSlot>(o: HeapObject): JSFunctionWithPrototypeSlot labels CastError { return HeapObjectToJSFunctionWithPrototypeSlot(o) otherwise CastError; } Cast<BigInt>(o: HeapObject): BigInt labels CastError { if (IsBigInt(o)) return %RawDownCast<BigInt>(o); goto CastError; } Cast<JSRegExpResult>(implicit context: Context)(o: HeapObject): JSRegExpResult labels CastError { if (regexp::IsRegExpResult(o)) return %RawDownCast<JSRegExpResult>(o); goto CastError; } Cast<JSSloppyArgumentsObject>(implicit context: Context)(o: HeapObject): JSSloppyArgumentsObject labels CastError { const map: Map = o.map; if (IsFastAliasedArgumentsMap(map) || IsSloppyArgumentsMap(map) || IsSlowAliasedArgumentsMap(map)) { return %RawDownCast<JSSloppyArgumentsObject>(o); } goto CastError; } Cast<JSStrictArgumentsObject>(implicit context: Context)(o: HeapObject): JSStrictArgumentsObject labels CastError { const map: Map = o.map; if (!IsStrictArgumentsMap(map)) goto CastError; return %RawDownCast<JSStrictArgumentsObject>(o); } Cast<JSArgumentsObjectWithLength>(implicit context: Context)(o: HeapObject): JSArgumentsObjectWithLength labels CastError { typeswitch (o) { case (o: JSStrictArgumentsObject): { return o; } case (o: JSSloppyArgumentsObject): { return o; } case (HeapObject): { goto CastError; } } } Cast<FastJSRegExp>(implicit context: Context)(o: HeapObject): FastJSRegExp labels CastError { // TODO(jgruber): Remove or redesign this. There is no single 'fast' regexp, // the conditions to make a regexp object fast differ based on the callsite. // For now, run the strict variant since replace (the only current callsite) // accesses flag getters. if (regexp::IsFastRegExpStrict(o)) { return %RawDownCast<FastJSRegExp>(o); } goto CastError; } Cast<FastJSArray>(implicit context: Context)(o: HeapObject): FastJSArray labels CastError { if (IsForceSlowPath()) goto CastError; if (!Is<JSArray>(o)) goto CastError; // Bailout if receiver has slow elements. const map: Map = o.map; const elementsKind: ElementsKind = LoadMapElementsKind(map); if (!IsFastElementsKind(elementsKind)) goto CastError; // Verify that our prototype is the initial array prototype. if (!IsPrototypeInitialArrayPrototype(map)) goto CastError; if (IsNoElementsProtectorCellInvalid()) goto CastError; return %RawDownCast<FastJSArray>(o); } Cast<FastJSArrayForRead>(implicit context: Context)(o: HeapObject): FastJSArrayForRead labels CastError { if (!Is<JSArray>(o)) goto CastError; // Bailout if receiver has slow elements. const map: Map = o.map; const elementsKind: ElementsKind = LoadMapElementsKind(map); if (!IsElementsKindLessThanOrEqual( elementsKind, ElementsKind::LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND)) goto CastError; // Verify that our prototype is the initial array prototype. if (!IsPrototypeInitialArrayPrototype(map)) goto CastError; if (IsNoElementsProtectorCellInvalid()) goto CastError; return %RawDownCast<FastJSArrayForRead>(o); } Cast<FastJSArrayForCopy>(implicit context: Context)(o: HeapObject): FastJSArrayForCopy labels CastError { if (IsArraySpeciesProtectorCellInvalid()) goto CastError; // TODO(victorgomes): Check if we can cast from FastJSArrayForRead instead. const a = Cast<FastJSArray>(o) otherwise CastError; return %RawDownCast<FastJSArrayForCopy>(a); } Cast<FastJSArrayForConcat>(implicit context: Context)(o: HeapObject): FastJSArrayForConcat labels CastError { if (IsIsConcatSpreadableProtectorCellInvalid()) goto CastError; const a = Cast<FastJSArrayForCopy>(o) otherwise CastError; return %RawDownCast<FastJSArrayForConcat>(a); } Cast<FastJSArrayWithNoCustomIteration>(implicit context: Context)( o: HeapObject): FastJSArrayWithNoCustomIteration labels CastError { if (IsArrayIteratorProtectorCellInvalid()) goto CastError; const a = Cast<FastJSArray>(o) otherwise CastError; return %RawDownCast<FastJSArrayWithNoCustomIteration>(a); } Cast<FastJSArrayForReadWithNoCustomIteration>(implicit context: Context)( o: HeapObject): FastJSArrayForReadWithNoCustomIteration labels CastError { if (IsArrayIteratorProtectorCellInvalid()) goto CastError; const a = Cast<FastJSArrayForRead>(o) otherwise CastError; return %RawDownCast<FastJSArrayForReadWithNoCustomIteration>(a); } macro Cast<T: type>(o: String): T labels CastError; Cast<SeqOneByteString>(o: HeapObject): SeqOneByteString labels CastError { return Cast<SeqOneByteString>(Cast<String>(o) otherwise CastError) otherwise CastError; } Cast<SeqOneByteString>(o: String): SeqOneByteString labels CastError { const instanceType = o.StringInstanceType(); // Using & instead of && enables Turbofan to merge the two checks into one. if (!(instanceType.representation == StringRepresentationTag::kSeqStringTag & instanceType.is_one_byte)) { goto CastError; } return %RawDownCast<SeqOneByteString>(o); } Cast<SeqTwoByteString>(o: HeapObject): SeqTwoByteString labels CastError { return Cast<SeqTwoByteString>(Cast<String>(o) otherwise CastError) otherwise CastError; } Cast<SeqTwoByteString>(o: String): SeqTwoByteString labels CastError { const instanceType = o.StringInstanceType(); // Using & instead of && enables Turbofan to merge the two checks into one. if (!(instanceType.representation == StringRepresentationTag::kSeqStringTag & !instanceType.is_one_byte)) { goto CastError; } return %RawDownCast<SeqTwoByteString>(o); } Cast<ThinString>(o: HeapObject): ThinString labels CastError { return Cast<ThinString>(Cast<String>(o) otherwise CastError) otherwise CastError; } Cast<ThinString>(o: String): ThinString labels CastError { const instanceType = o.StringInstanceType(); if (instanceType.representation != StringRepresentationTag::kThinStringTag) { goto CastError; } return %RawDownCast<ThinString>(o); } Cast<ConsString>(o: HeapObject): ConsString labels CastError { return Cast<ConsString>(Cast<String>(o) otherwise CastError) otherwise CastError; } Cast<ConsString>(o: String): ConsString labels CastError { const instanceType = o.StringInstanceType(); if (instanceType.representation != StringRepresentationTag::kConsStringTag) { goto CastError; } return %RawDownCast<ConsString>(o); } Cast<SlicedString>(o: HeapObject): SlicedString labels CastError { return Cast<SlicedString>(Cast<String>(o) otherwise CastError) otherwise CastError; } Cast<SlicedString>(o: String): SlicedString labels CastError { const instanceType = o.StringInstanceType(); if (instanceType.representation != StringRepresentationTag::kSlicedStringTag) { goto CastError; } return %RawDownCast<SlicedString>(o); } Cast<ExternalOneByteString>(o: HeapObject): ExternalOneByteString labels CastError { return Cast<ExternalOneByteString>(Cast<String>(o) otherwise CastError) otherwise CastError; } Cast<ExternalOneByteString>(o: String): ExternalOneByteString labels CastError { const instanceType = o.StringInstanceType(); // Using & instead of && enables Turbofan to merge the two checks into one. if (!(instanceType.representation == StringRepresentationTag::kExternalStringTag & instanceType.is_one_byte)) { goto CastError; } return %RawDownCast<ExternalOneByteString>(o); } Cast<ExternalTwoByteString>(o: HeapObject): ExternalTwoByteString labels CastError { return Cast<ExternalTwoByteString>(Cast<String>(o) otherwise CastError) otherwise CastError; } Cast<ExternalTwoByteString>(o: String): ExternalTwoByteString labels CastError { const instanceType = o.StringInstanceType(); // Using & instead of && enables Turbofan to merge the two checks into one. if (!(instanceType.representation == StringRepresentationTag::kExternalStringTag & !instanceType.is_one_byte)) { goto CastError; } return %RawDownCast<ExternalTwoByteString>(o); } Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null labels CastError { typeswitch (o) { case (o: Null): { return o; } case (o: JSReceiver): { return o; } case (HeapObject): { goto CastError; } } } Cast<Smi|PromiseReaction>(o: Object): Smi|PromiseReaction labels CastError { typeswitch (o) { case (o: Smi): { return o; } case (o: PromiseReaction): { return o; } case (Object): { goto CastError; } } } Cast<String|Callable>(implicit context: Context)(o: Object): String| Callable labels CastError { typeswitch (o) { case (o: String): { return o; } case (o: Callable): { return o; } case (Object): { goto CastError; } } } Cast<Zero|PromiseReaction>(implicit context: Context)(o: Object): Zero| PromiseReaction labels CastError { typeswitch (o) { case (o: Zero): { return o; } case (o: PromiseReaction): { return o; } case (Object): { goto CastError; } } } Cast<JSFunction|JSBoundFunction>(implicit context: Context)(o: Object): JSFunction|JSBoundFunction labels CastError { typeswitch (o) { case (o: JSFunction): { return o; } case (o: JSBoundFunction): { return o; } case (Object): { goto CastError; } } } Cast<FixedArray|Undefined>(o: HeapObject): FixedArray| Undefined labels CastError { typeswitch (o) { case (o: Undefined): { return o; } case (o: FixedArray): { return o; } case (Object): { goto CastError; } } } Cast<JSProxy|Null>(o: HeapObject): JSProxy|Null labels CastError { typeswitch (o) { case (o: Null): { return o; } case (o: JSProxy): { return o; } case (Object): { goto CastError; } } } macro Is<A : type extends Object, B : type extends Object>( implicit context: Context)(o: B): bool { Cast<A>(o) otherwise return false; return true; } macro UnsafeCast<A : type extends Object>(implicit context: Context)(o: Object): A { assert(Is<A>(o)); return %RawDownCast<A>(o); } macro UnsafeConstCast<T: type>(r: const &T):&T { return %RawDownCast<&T>(r); } UnsafeCast<RegExpMatchInfo>(implicit context: Context)(o: Object): RegExpMatchInfo { assert(Is<FixedArray>(o)); return %RawDownCast<RegExpMatchInfo>(o); } macro UnsafeCast<A : type extends WeakHeapObject>(o: A|Object): A { assert(IsWeakOrCleared(o)); return %RawDownCast<A>(o); } macro CastOrDefault<T: type, Arg: type, Default: type>(implicit context: Context)( x: Arg, default: Default): T|Default { return Cast<T>(x) otherwise return default; } // This is required for casting MaybeObject to Object. Cast<Object>(o: Object): Object labels _CastError { return o; }