[torque] Add HeapSlice location references to Torque
This CL adds a LocationReference specifically for slices to Torque. This allows us to safely reference arrays in objects and pass around such references. For an array of T-typed elements, referencing yields a Slice<T>. In addition, the traditional element access syntax ('o.array[i]') now internally produces a slice, indexes it at 'i' and dereferences the resulting HeapReference. Bug: v8:7793 Change-Id: I4af58e4d2feac547c55a1f6f9350a6c510383df2 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1771782 Commit-Queue: Georg Schmid <gsps@google.com> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#63479}
This commit is contained in:
parent
f4a3028b59
commit
cbf015025a
@ -1140,6 +1140,7 @@ extern class WasmExceptionTag extends Struct {
|
||||
}
|
||||
|
||||
const kTaggedSize: constexpr int31 generates 'kTaggedSize';
|
||||
const kDoubleSize: constexpr int31 generates 'kDoubleSize';
|
||||
|
||||
const kSmiTagSize: constexpr int31 generates 'kSmiTagSize';
|
||||
const V8_INFINITY: constexpr float64 generates 'V8_INFINITY';
|
||||
@ -2539,6 +2540,7 @@ 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;
|
||||
|
@ -6,13 +6,16 @@ namespace torque_internal {
|
||||
// TODO(gsps): Synthesize SizeOf<T> in the compiler
|
||||
|
||||
macro SizeOf<T: type>(): constexpr int31;
|
||||
SizeOf<Smi>(): constexpr int31 {
|
||||
SizeOf<Object>(): constexpr int31 {
|
||||
return kTaggedSize;
|
||||
}
|
||||
SizeOf<float64>(): constexpr int31 {
|
||||
return kDoubleSize;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// that might lead to unsoundness when used incorrectly. Unsafe markers should
|
||||
// therefore not be instantiated anywhere outside of this namespace.
|
||||
struct Unsafe {}
|
||||
|
||||
struct Reference<T: type> {
|
||||
@ -30,7 +33,7 @@ namespace torque_internal {
|
||||
}
|
||||
|
||||
struct Slice<T: type> {
|
||||
Access(index: intptr):&T labels OutOfBounds {
|
||||
TryAtIndex(index: intptr):&T labels OutOfBounds {
|
||||
if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
|
||||
return UnsafeNewReference<T>(
|
||||
this.object, this.offset + index * SizeOf<T>());
|
||||
@ -39,6 +42,20 @@ namespace torque_internal {
|
||||
}
|
||||
}
|
||||
|
||||
AtIndex(index: intptr):&T {
|
||||
return this.TryAtIndex(index) otherwise unreachable;
|
||||
}
|
||||
|
||||
AtIndex(index: constexpr int31):&T {
|
||||
const i: intptr = Convert<intptr>(index);
|
||||
return this.TryAtIndex(i) otherwise unreachable;
|
||||
}
|
||||
|
||||
AtIndex(index: Smi):&T {
|
||||
const i: intptr = Convert<intptr>(index);
|
||||
return this.TryAtIndex(i) otherwise unreachable;
|
||||
}
|
||||
|
||||
Iterator(): SliceIterator<T> {
|
||||
const end = this.offset + this.length * SizeOf<T>();
|
||||
return SliceIterator<T>{
|
||||
|
@ -247,6 +247,9 @@ class FixedDoubleArray : public FixedArrayBase {
|
||||
|
||||
DECL_CAST(FixedDoubleArray)
|
||||
|
||||
// Start offset of elements.
|
||||
static constexpr int kFloatsOffset = kHeaderSize;
|
||||
|
||||
// Maximally allowed length of a FixedDoubleArray.
|
||||
static const int kMaxLength = (kMaxSize - kHeaderSize) / kDoubleSize;
|
||||
static_assert(Internals::IsValidSmi(kMaxLength),
|
||||
|
@ -52,6 +52,7 @@ static const char* const CONST_INT32_TYPE_STRING = "constexpr int32";
|
||||
static const char* const CONST_FLOAT64_TYPE_STRING = "constexpr float64";
|
||||
static const char* const TORQUE_INTERNAL_NAMESPACE_STRING = "torque_internal";
|
||||
static const char* const REFERENCE_TYPE_STRING = "Reference";
|
||||
static const char* const SLICE_TYPE_STRING = "Slice";
|
||||
static const char* const STRUCT_NAMESPACE_STRING = "_struct";
|
||||
|
||||
inline bool IsConstexprName(const std::string& name) {
|
||||
|
@ -708,8 +708,13 @@ void CSAGenerator::EmitInstruction(const UnsafeCastInstruction& instruction,
|
||||
void CSAGenerator::EmitInstruction(
|
||||
const CreateFieldReferenceInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
const Field& field =
|
||||
instruction.class_type->LookupField(instruction.field_name);
|
||||
base::Optional<const ClassType*> class_type =
|
||||
instruction.type->ClassSupertype();
|
||||
if (!class_type.has_value()) {
|
||||
ReportError("Cannot create field reference of type ", instruction.type,
|
||||
" which does not inherit from a class type");
|
||||
}
|
||||
const Field& field = class_type.value()->LookupField(instruction.field_name);
|
||||
std::string offset_name = FreshNodeName();
|
||||
stack->Push(offset_name);
|
||||
|
||||
|
@ -1727,14 +1727,13 @@ Callable* ImplementationVisitor::LookupCallable(
|
||||
}
|
||||
|
||||
Method* ImplementationVisitor::LookupMethod(
|
||||
const std::string& name, LocationReference this_reference,
|
||||
const std::string& name, const AggregateType* receiver_type,
|
||||
const Arguments& arguments, const TypeVector& specialization_types) {
|
||||
TypeVector types(arguments.parameters.ComputeTypeVector());
|
||||
types.insert(types.begin(), this_reference.ReferencedType());
|
||||
return Method::cast(LookupCallable(
|
||||
{{}, name},
|
||||
AggregateType::cast(this_reference.ReferencedType())->Methods(name),
|
||||
types, arguments.labels, specialization_types));
|
||||
types.insert(types.begin(), receiver_type);
|
||||
return Method::cast(LookupCallable({{}, name}, receiver_type->Methods(name),
|
||||
types, arguments.labels,
|
||||
specialization_types));
|
||||
}
|
||||
|
||||
const Type* ImplementationVisitor::GetCommonType(const Type* left,
|
||||
@ -1855,7 +1854,33 @@ LocationReference ImplementationVisitor::GetLocationReference(
|
||||
LanguageServerData::AddDefinition(expr->field->pos, field.pos);
|
||||
}
|
||||
if (field.index) {
|
||||
return LocationReference::IndexedFieldAccess(object_result, fieldname);
|
||||
assembler().Emit(
|
||||
CreateFieldReferenceInstruction{object_result.type(), fieldname});
|
||||
// Fetch the length from the object
|
||||
{
|
||||
StackScope length_scope(this);
|
||||
// Get a reference to the length
|
||||
const Field* index_field = field.index.value();
|
||||
GenerateCopy(object_result);
|
||||
assembler().Emit(CreateFieldReferenceInstruction{
|
||||
object_result.type(), index_field->name_and_type.name});
|
||||
VisitResult length_reference(
|
||||
TypeOracle::GetReferenceType(index_field->name_and_type.type),
|
||||
assembler().TopRange(2));
|
||||
|
||||
// Load the length from the reference and convert it to intptr
|
||||
VisitResult length = GenerateFetchFromLocation(
|
||||
LocationReference::HeapReference(length_reference));
|
||||
VisitResult converted_length =
|
||||
GenerateCall("Convert", {{length}, {}},
|
||||
{TypeOracle::GetIntPtrType(), length.type()}, false);
|
||||
DCHECK_EQ(converted_length.stack_range().Size(), 1);
|
||||
length_scope.Yield(converted_length);
|
||||
}
|
||||
const Type* slice_type =
|
||||
TypeOracle::GetSliceType(field.name_and_type.type);
|
||||
return LocationReference::HeapSlice(
|
||||
VisitResult(slice_type, assembler().TopRange(3)));
|
||||
} else {
|
||||
assembler().Emit(
|
||||
CreateFieldReferenceInstruction{*class_type, fieldname});
|
||||
@ -1873,8 +1898,13 @@ LocationReference ImplementationVisitor::GetLocationReference(
|
||||
ElementAccessExpression* expr) {
|
||||
LocationReference reference = GetLocationReference(expr->array);
|
||||
VisitResult index = Visit(expr->index);
|
||||
if (reference.IsIndexedFieldAccess()) {
|
||||
return LocationReference::IndexedFieldIndexedAccess(reference, index);
|
||||
if (reference.IsHeapSlice()) {
|
||||
Arguments arguments{{index}, {}};
|
||||
const AggregateType* slice_type =
|
||||
AggregateType::cast(reference.heap_slice().type());
|
||||
Method* method = LookupMethod("AtIndex", slice_type, arguments, {});
|
||||
return LocationReference::HeapReference(
|
||||
GenerateCall(method, reference, arguments, {}, false));
|
||||
} else {
|
||||
return LocationReference::ArrayAccess(GenerateFetchFromLocation(reference),
|
||||
index);
|
||||
@ -1974,7 +2004,7 @@ VisitResult ImplementationVisitor::GenerateFetchFromLocation(
|
||||
DCHECK_EQ(1, LoweredSlotCount(reference.ReferencedType()));
|
||||
return VisitResult(reference.ReferencedType(), assembler().TopRange(1));
|
||||
} else {
|
||||
if (reference.IsIndexedFieldAccess()) {
|
||||
if (reference.IsHeapSlice()) {
|
||||
ReportError(
|
||||
"fetching a value directly from an indexed field isn't allowed");
|
||||
}
|
||||
@ -2002,12 +2032,19 @@ void ImplementationVisitor::GenerateAssignToLocation(
|
||||
if (reference.binding()) {
|
||||
(*reference.binding())->SetWritten();
|
||||
}
|
||||
} else if (reference.IsIndexedFieldAccess()) {
|
||||
} else if (reference.IsHeapSlice()) {
|
||||
ReportError("assigning a value directly to an indexed field isn't allowed");
|
||||
} else if (reference.IsHeapReference()) {
|
||||
const Type* referenced_type = reference.ReferencedType();
|
||||
GenerateCopy(reference.heap_reference());
|
||||
GenerateImplicitConvert(referenced_type, assignment_value);
|
||||
VisitResult converted_assignment_value =
|
||||
GenerateImplicitConvert(referenced_type, assignment_value);
|
||||
if (referenced_type == TypeOracle::GetFloat64Type()) {
|
||||
VisitResult silenced_float_value =
|
||||
GenerateCall("Float64SilenceNaN", {{assignment_value}, {}});
|
||||
assembler().Poke(converted_assignment_value.stack_range(),
|
||||
silenced_float_value.stack_range(), referenced_type);
|
||||
}
|
||||
assembler().Emit(StoreReferenceInstruction{referenced_type});
|
||||
} else {
|
||||
DCHECK(reference.IsTemporary());
|
||||
@ -2126,8 +2163,8 @@ VisitResult ImplementationVisitor::GenerateCall(
|
||||
if (this_reference) {
|
||||
DCHECK(callable->IsMethod());
|
||||
Method* method = Method::cast(callable);
|
||||
// By now, the this reference should either be a variable or
|
||||
// a temporary, in both cases the fetch of the VisitResult should succeed.
|
||||
// By now, the this reference should either be a variable, a temporary or
|
||||
// a Slice. In either case the fetch of the VisitResult should succeed.
|
||||
VisitResult this_value = this_reference->GetVisitResult();
|
||||
if (method->ShouldBeInlined()) {
|
||||
if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) {
|
||||
@ -2345,6 +2382,7 @@ VisitResult ImplementationVisitor::Visit(CallExpression* expr,
|
||||
if (auto* loc_expr = LocationExpression::DynamicCast(expr->arguments[0])) {
|
||||
LocationReference ref = GetLocationReference(loc_expr);
|
||||
if (ref.IsHeapReference()) return scope.Yield(ref.heap_reference());
|
||||
if (ref.IsHeapSlice()) return scope.Yield(ref.heap_slice());
|
||||
}
|
||||
ReportError("Unable to create a heap reference.");
|
||||
}
|
||||
@ -2398,7 +2436,7 @@ VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) {
|
||||
DCHECK_EQ(expr->method->namespace_qualification.size(), 0);
|
||||
QualifiedName qualified_name = QualifiedName(method_name);
|
||||
Callable* callable = nullptr;
|
||||
callable = LookupMethod(method_name, target, arguments, {});
|
||||
callable = LookupMethod(method_name, target_type, arguments, {});
|
||||
if (GlobalContext::collect_language_server_data()) {
|
||||
LanguageServerData::AddDefinition(expr->method->name->pos,
|
||||
callable->IdentifierPosition());
|
||||
@ -3665,6 +3703,16 @@ void ReportAllUnusedMacros() {
|
||||
if (macro->IsTorqueMacro() && TorqueMacro::cast(macro)->IsExportedToCSA()) {
|
||||
continue;
|
||||
}
|
||||
// TODO(gsps): Mark methods of generic structs used if they are used in any
|
||||
// instantiation
|
||||
if (Method* method = Method::DynamicCast(macro)) {
|
||||
if (StructType* struct_type =
|
||||
StructType::DynamicCast(method->aggregate_type())) {
|
||||
if (struct_type->GetSpecializedFrom().has_value()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ignored_prefixes = {"Convert<", "Cast<",
|
||||
"FromConstexpr<"};
|
||||
|
@ -58,6 +58,15 @@ class LocationReference {
|
||||
result.heap_reference_ = std::move(heap_reference);
|
||||
return result;
|
||||
}
|
||||
// A reference to an array on the heap. That is, a tagged value, an offset to
|
||||
// encode an inner pointer, and the number of elements.
|
||||
static LocationReference HeapSlice(VisitResult heap_slice) {
|
||||
LocationReference result;
|
||||
DCHECK(StructType::MatchUnaryGeneric(heap_slice.type(),
|
||||
TypeOracle::GetSliceGeneric()));
|
||||
result.heap_slice_ = std::move(heap_slice);
|
||||
return result;
|
||||
}
|
||||
static LocationReference ArrayAccess(VisitResult base, VisitResult offset) {
|
||||
LocationReference result;
|
||||
result.eval_function_ = std::string{"[]"};
|
||||
@ -71,26 +80,6 @@ class LocationReference {
|
||||
result.eval_function_ = "." + fieldname;
|
||||
result.assign_function_ = "." + fieldname + "=";
|
||||
result.call_arguments_ = {object};
|
||||
result.index_field_ = base::nullopt;
|
||||
return result;
|
||||
}
|
||||
static LocationReference IndexedFieldIndexedAccess(
|
||||
const LocationReference& indexed_field, VisitResult index) {
|
||||
LocationReference result;
|
||||
DCHECK(indexed_field.IsIndexedFieldAccess());
|
||||
std::string fieldname = *indexed_field.index_field_;
|
||||
result.eval_function_ = "." + fieldname + "[]";
|
||||
result.assign_function_ = "." + fieldname + "[]=";
|
||||
result.call_arguments_ = indexed_field.call_arguments_;
|
||||
result.call_arguments_.push_back(index);
|
||||
result.index_field_ = fieldname;
|
||||
return result;
|
||||
}
|
||||
static LocationReference IndexedFieldAccess(VisitResult object,
|
||||
std::string fieldname) {
|
||||
LocationReference result;
|
||||
result.call_arguments_ = {object};
|
||||
result.index_field_ = fieldname;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -111,17 +100,26 @@ class LocationReference {
|
||||
DCHECK(IsHeapReference());
|
||||
return *heap_reference_;
|
||||
}
|
||||
bool IsHeapSlice() const { return heap_slice_.has_value(); }
|
||||
const VisitResult& heap_slice() const {
|
||||
DCHECK(IsHeapSlice());
|
||||
return *heap_slice_;
|
||||
}
|
||||
|
||||
const Type* ReferencedType() const {
|
||||
if (IsHeapReference()) {
|
||||
return *StructType::MatchUnaryGeneric(heap_reference().type(),
|
||||
TypeOracle::GetReferenceGeneric());
|
||||
} else if (IsHeapSlice()) {
|
||||
return *StructType::MatchUnaryGeneric(heap_slice().type(),
|
||||
TypeOracle::GetSliceGeneric());
|
||||
}
|
||||
return GetVisitResult().type();
|
||||
}
|
||||
|
||||
const VisitResult& GetVisitResult() const {
|
||||
if (IsVariableAccess()) return variable();
|
||||
if (IsHeapSlice()) return heap_slice();
|
||||
DCHECK(IsTemporary());
|
||||
return temporary();
|
||||
}
|
||||
@ -132,13 +130,6 @@ class LocationReference {
|
||||
return *temporary_description_;
|
||||
}
|
||||
|
||||
bool IsArrayField() const { return index_field_.has_value(); }
|
||||
bool IsIndexedFieldAccess() const {
|
||||
return IsArrayField() && !IsCallAccess();
|
||||
}
|
||||
bool IsIndexedFieldIndexedAccess() const {
|
||||
return IsArrayField() && IsCallAccess();
|
||||
}
|
||||
bool IsCallAccess() const {
|
||||
bool is_call_access = eval_function_.has_value();
|
||||
DCHECK_EQ(is_call_access, assign_function_.has_value());
|
||||
@ -166,10 +157,10 @@ class LocationReference {
|
||||
base::Optional<VisitResult> temporary_;
|
||||
base::Optional<std::string> temporary_description_;
|
||||
base::Optional<VisitResult> heap_reference_;
|
||||
base::Optional<VisitResult> heap_slice_;
|
||||
base::Optional<std::string> eval_function_;
|
||||
base::Optional<std::string> assign_function_;
|
||||
VisitResultVector call_arguments_;
|
||||
base::Optional<std::string> index_field_;
|
||||
base::Optional<Binding<LocalValue>*> binding_;
|
||||
|
||||
LocationReference() = default;
|
||||
@ -573,7 +564,8 @@ class ImplementationVisitor {
|
||||
const Arguments& arguments,
|
||||
const TypeVector& specialization_types);
|
||||
|
||||
Method* LookupMethod(const std::string& name, LocationReference target,
|
||||
Method* LookupMethod(const std::string& name,
|
||||
const AggregateType* receiver_type,
|
||||
const Arguments& arguments,
|
||||
const TypeVector& specialization_types);
|
||||
|
||||
|
@ -292,26 +292,23 @@ void UnsafeCastInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
|
||||
void CreateFieldReferenceInstruction::TypeInstruction(
|
||||
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
|
||||
ExpectSubtype(stack->Pop(), class_type);
|
||||
stack->Push(TypeOracle::GetHeapObjectType());
|
||||
ExpectSubtype(stack->Top(), type);
|
||||
stack->Push(TypeOracle::GetIntPtrType());
|
||||
}
|
||||
|
||||
// TODO(gsps): Remove in favor of a method on Reference<T>
|
||||
void LoadReferenceInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
ExpectType(TypeOracle::GetIntPtrType(), stack->Pop());
|
||||
ExpectType(TypeOracle::GetHeapObjectType(), stack->Pop());
|
||||
ExpectSubtype(stack->Pop(), TypeOracle::GetHeapObjectType());
|
||||
DCHECK_EQ(std::vector<const Type*>{type}, LowerType(type));
|
||||
stack->Push(type);
|
||||
}
|
||||
|
||||
// TODO(gsps): Remove in favor of a method on Reference<T>
|
||||
void StoreReferenceInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
ExpectSubtype(stack->Pop(), type);
|
||||
ExpectType(TypeOracle::GetIntPtrType(), stack->Pop());
|
||||
ExpectType(TypeOracle::GetHeapObjectType(), stack->Pop());
|
||||
ExpectSubtype(stack->Pop(), TypeOracle::GetHeapObjectType());
|
||||
}
|
||||
|
||||
bool CallRuntimeInstruction::IsBlockTerminator() const {
|
||||
|
@ -206,10 +206,9 @@ struct NamespaceConstantInstruction : InstructionBase {
|
||||
|
||||
struct CreateFieldReferenceInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
CreateFieldReferenceInstruction(const ClassType* class_type,
|
||||
std::string field_name)
|
||||
: class_type(class_type), field_name(std::move(field_name)) {}
|
||||
const ClassType* class_type;
|
||||
CreateFieldReferenceInstruction(const Type* type, std::string field_name)
|
||||
: type(type), field_name(std::move(field_name)) {}
|
||||
const Type* type;
|
||||
std::string field_name;
|
||||
};
|
||||
|
||||
|
@ -72,11 +72,20 @@ class TypeOracle : public ContextualClass<TypeOracle> {
|
||||
{TORQUE_INTERNAL_NAMESPACE_STRING}, REFERENCE_TYPE_STRING));
|
||||
}
|
||||
|
||||
static GenericStructType* GetSliceGeneric() {
|
||||
return Declarations::LookupUniqueGenericStructType(
|
||||
QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, SLICE_TYPE_STRING));
|
||||
}
|
||||
|
||||
static const StructType* GetReferenceType(const Type* referenced_type) {
|
||||
return GetGenericStructTypeInstance(GetReferenceGeneric(),
|
||||
{referenced_type});
|
||||
}
|
||||
|
||||
static const StructType* GetSliceType(const Type* referenced_type) {
|
||||
return GetGenericStructTypeInstance(GetSliceGeneric(), {referenced_type});
|
||||
}
|
||||
|
||||
static const std::vector<const BuiltinPointerType*>&
|
||||
AllBuiltinPointerTypes() {
|
||||
return Get().all_builtin_pointer_types_;
|
||||
|
@ -263,7 +263,7 @@ const Field& AggregateType::LookupFieldInternal(const std::string& name) const {
|
||||
return parent_class->LookupField(name);
|
||||
}
|
||||
}
|
||||
ReportError("no field ", name, " found");
|
||||
ReportError("no field ", name, " found in ", this->ToString());
|
||||
}
|
||||
|
||||
const Field& AggregateType::LookupField(const std::string& name) const {
|
||||
|
@ -292,7 +292,7 @@ T* CheckNotNull(T* x) {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline std::ostream& operator<<(std::ostream& os, Stack<T>& t) {
|
||||
inline std::ostream& operator<<(std::ostream& os, const Stack<T>& t) {
|
||||
os << "Stack{";
|
||||
PrintCommaSeparatedList(os, t);
|
||||
os << "}";
|
||||
|
@ -498,6 +498,22 @@ TEST(TestSlices) {
|
||||
ft.Call();
|
||||
}
|
||||
|
||||
TEST(TestSliceEnumeration) {
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate(CcTest::i_isolate());
|
||||
i::HandleScope scope(isolate);
|
||||
Handle<Context> context =
|
||||
Utils::OpenHandle(*v8::Isolate::GetCurrent()->GetCurrentContext());
|
||||
CodeAssemblerTester asm_tester(isolate);
|
||||
TestTorqueAssembler m(asm_tester.state());
|
||||
{
|
||||
m.TestSliceEnumeration(m.UncheckedCast<Context>(m.HeapConstant(context)));
|
||||
m.Return(m.UndefinedConstant());
|
||||
}
|
||||
FunctionTester ft(asm_tester.GenerateCode(), 0);
|
||||
ft.Call();
|
||||
}
|
||||
|
||||
TEST(TestStaticAssert) {
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate(CcTest::i_isolate());
|
||||
|
@ -843,27 +843,60 @@ namespace test {
|
||||
|
||||
@export
|
||||
macro TestSlices() {
|
||||
const fixedArray: FixedArray = AllocateZeroedFixedArray(3);
|
||||
// TODO(gsps): Directly reference fixedArray.objects once supported
|
||||
let slice = torque_internal::UnsafeNewSlice<Smi>(
|
||||
fixedArray,
|
||||
(& fixedArray.length).offset + torque_internal::SizeOf<Smi>(), 3);
|
||||
const it = TestIterator{count: 3};
|
||||
const a = new FixedArray{map: kFixedArrayMap, length: 3, objects: ...it};
|
||||
check(a.length == 3);
|
||||
|
||||
const oneTwoThree = Convert<Smi>(123);
|
||||
a.objects[0] = oneTwoThree;
|
||||
const firstRef:&Object = & a.objects[0];
|
||||
check(TaggedEqual(* firstRef, oneTwoThree));
|
||||
|
||||
const slice: torque_internal::Slice<Object> = & a.objects;
|
||||
const firstRefAgain:&Object = slice.TryAtIndex(0) otherwise unreachable;
|
||||
check(TaggedEqual(* firstRefAgain, oneTwoThree));
|
||||
|
||||
const threeTwoOne = Convert<Smi>(321);
|
||||
* firstRefAgain = threeTwoOne;
|
||||
check(TaggedEqual(a.objects[0], threeTwoOne));
|
||||
|
||||
// *slice; // error, not allowed
|
||||
// a.objects; // error, not allowed
|
||||
// a.objects = slice; // error, not allowed
|
||||
|
||||
// TODO(gsps): Currently errors, but should be allowed:
|
||||
// const _sameSlice: torque_internal::Slice<Object> = &(*slice);
|
||||
// (*slice)[0] : Smi
|
||||
}
|
||||
|
||||
@export
|
||||
macro TestSliceEnumeration(implicit context: Context)(): Undefined {
|
||||
const fixedArray: FixedArray = AllocateZeroedFixedArray(3);
|
||||
for (let i: intptr = 0; i < 3; i++) {
|
||||
check(UnsafeCast<Smi>(fixedArray.objects[i]) == 0);
|
||||
fixedArray.objects[i] = Convert<Smi>(i) + 3;
|
||||
}
|
||||
|
||||
let slice = & fixedArray.objects;
|
||||
for (let i: intptr = 0; i < slice.length; i++) {
|
||||
let ref = slice.Access(i) otherwise unreachable;
|
||||
check(* ref == 0);
|
||||
* ref = Convert<Smi>(i) + 7;
|
||||
let ref = slice.TryAtIndex(i) otherwise unreachable;
|
||||
const value = UnsafeCast<Smi>(* ref);
|
||||
check(value == Convert<Smi>(i) + 3);
|
||||
* ref = value + 4;
|
||||
}
|
||||
|
||||
let it = slice.Iterator();
|
||||
let count: Smi = 0;
|
||||
while (true) {
|
||||
let ref = it.Next() otherwise break;
|
||||
check(* ref == count + 7);
|
||||
const value = UnsafeCast<Smi>(* ref);
|
||||
check(value == count + 7);
|
||||
count++;
|
||||
}
|
||||
check(count == 3);
|
||||
check(it.Empty());
|
||||
|
||||
return Undefined;
|
||||
}
|
||||
|
||||
@export
|
||||
|
Loading…
Reference in New Issue
Block a user