// 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. namespace torque_internal { // Unsafe is a marker that we require to be passed when calling internal APIs // that might lead to unsoundness when used incorrectly. Unsafe markers should // therefore not be instantiated anywhere outside of this namespace. struct Unsafe {} // Size of a type in memory (on the heap). For class types, this is the size // of the pointer, not of the instance. intrinsic %SizeOf(): constexpr int31; struct Reference { const object: HeapObject; const offset: intptr; unsafeMarker: Unsafe; } type ConstReference extends Reference; type MutableReference extends ConstReference; namespace unsafe { macro NewReference(object: HeapObject, offset: intptr):&T { return %RawDownCast<&T>( Reference{object: object, offset: offset, unsafeMarker: Unsafe {}}); } } // namespace unsafe struct Slice { macro TryAtIndex(index: intptr):&T labels OutOfBounds { if (Convert(index) < Convert(this.length)) { return unsafe::NewReference( this.object, this.offset + index * %SizeOf()); } else { goto OutOfBounds; } } macro AtIndex(index: intptr):&T { return this.TryAtIndex(index) otherwise unreachable; } macro AtIndex(index: uintptr):&T { return this.TryAtIndex(Convert(index)) otherwise unreachable; } macro AtIndex(index: constexpr int31):&T { const i: intptr = Convert(index); return this.TryAtIndex(i) otherwise unreachable; } macro AtIndex(index: Smi):&T { const i: intptr = Convert(index); return this.TryAtIndex(i) otherwise unreachable; } macro Iterator(): SliceIterator { const end = this.offset + this.length * %SizeOf(); return SliceIterator{ object: this.object, start: this.offset, end: end, unsafeMarker: Unsafe {} }; } macro Iterator(startIndex: intptr, endIndex: intptr): SliceIterator { check( Convert(endIndex) <= Convert(this.length) && Convert(startIndex) <= Convert(endIndex)); const start = this.offset + startIndex * %SizeOf(); const end = this.offset + endIndex * %SizeOf(); return SliceIterator{ object: this.object, start, end, unsafeMarker: Unsafe {} }; } const object: HeapObject; const offset: intptr; const length: intptr; unsafeMarker: Unsafe; } macro UnsafeNewSlice( object: HeapObject, offset: intptr, length: intptr): Slice { return Slice{ object: object, offset: offset, length: length, unsafeMarker: Unsafe {} }; } struct SliceIterator { macro Empty(): bool { return this.start == this.end; } macro Next(): T labels NoMore { return * this.NextReference() otherwise NoMore; } macro NextReference():&T labels NoMore { if (this.Empty()) { goto NoMore; } else { const result = unsafe::NewReference(this.object, this.start); this.start += %SizeOf(); return result; } } object: HeapObject; start: intptr; end: intptr; unsafeMarker: Unsafe; } macro AddIndexedFieldSizeToObjectSize( baseSize: intptr, arrayLength: intptr, fieldSize: constexpr int32): intptr { const arrayLength = Convert(arrayLength); const byteLength = TryInt32Mul(arrayLength, fieldSize) otherwise unreachable; return TryIntPtrAdd(baseSize, Convert(byteLength)) otherwise unreachable; } macro AlignTagged(x: intptr): intptr { // Round up to a multiple of kTaggedSize. return (x + kObjectAlignmentMask) & ~kObjectAlignmentMask; } macro IsTaggedAligned(x: intptr): bool { return (x & kObjectAlignmentMask) == 0; } macro ValidAllocationSize(sizeInBytes: intptr, map: Map): bool { if (sizeInBytes <= 0) return false; if (!IsTaggedAligned(sizeInBytes)) return false; const instanceSizeInWords = Convert(map.instance_size_in_words); return instanceSizeInWords == kVariableSizeSentinel || instanceSizeInWords * kTaggedSize == sizeInBytes; } type UninitializedHeapObject extends HeapObject; extern macro AllocateAllowLOS(intptr): UninitializedHeapObject; extern macro GetInstanceTypeMap(constexpr InstanceType): Map; macro Allocate(sizeInBytes: intptr, map: Map): UninitializedHeapObject { assert(ValidAllocationSize(sizeInBytes, map)); return AllocateAllowLOS(sizeInBytes); } macro InitializeFieldsFromIterator( target: Slice, originIterator: Iterator) { let targetIterator = target.Iterator(); let originIterator = originIterator; while (true) { const ref:&T = targetIterator.NextReference() otherwise break; * ref = originIterator.Next() otherwise unreachable; } } // Dummy implementations: do not initialize for UninitializedIterator. InitializeFieldsFromIterator( _target: Slice, _originIterator: UninitializedIterator) {} InitializeFieldsFromIterator( _target: Slice, _originIterator: UninitializedIterator) {} extern macro IsDoubleHole(HeapObject, intptr): bool; extern macro StoreDoubleHole(HeapObject, intptr); macro LoadFloat64OrHole(r:&float64_or_hole): float64_or_hole { return float64_or_hole{ is_hole: IsDoubleHole(r.object, r.offset - kHeapObjectTag), value: * unsafe::NewReference(r.object, r.offset) }; } macro StoreFloat64OrHole(r:&float64_or_hole, value: float64_or_hole) { if (value.is_hole) { StoreDoubleHole(r.object, r.offset - kHeapObjectTag); } else { * unsafe::NewReference(r.object, r.offset) = value.value; } } macro DownCastForTorqueClass(o: HeapObject): T labels CastError { const map = o.map; const minInstanceType = %MinInstanceType(); const maxInstanceType = %MaxInstanceType(); if constexpr (minInstanceType == maxInstanceType) { if constexpr (%ClassHasMapConstant()) { if (map != %GetClassMapConstant()) goto CastError; } else { if (map.instance_type != minInstanceType) goto CastError; } } else { const diff: int32 = maxInstanceType - minInstanceType; const offset = Convert(Convert(map.instance_type)) - Convert(Convert( FromConstexpr(minInstanceType))); if (Unsigned(offset) > Unsigned(diff)) goto CastError; } return %RawDownCast(o); } } // namespace torque_internal // Indicates that an array-field should not be initialized. // For safety reasons, this is only allowed for untagged types. struct UninitializedIterator {} // %RawDownCast should *never* be used anywhere in Torque code except for // in Torque-based UnsafeCast operators preceeded by an appropriate // type assert() intrinsic %RawDownCast(x: From): To; intrinsic %RawConstexprCast(f: From): To; intrinsic %MinInstanceType(): constexpr InstanceType; intrinsic %MaxInstanceType(): constexpr InstanceType; intrinsic %ClassHasMapConstant(): constexpr bool; intrinsic %GetClassMapConstant(): Map; struct IteratorSequence { macro Empty(): bool { return this.first.Empty() && this.second.Empty(); } macro Next(): T labels NoMore { return this.first.Next() otherwise return (this.second.Next() otherwise NoMore); } first: FirstIterator; second: SecondIterator; } macro IteratorSequence( first: FirstIterator, second: SecondIterator): IteratorSequence { return IteratorSequence{first, second}; }