Commit 9ddb8d9c authored by Seth Brenith's avatar Seth Brenith Committed by Commit Bot

[torque] Allow referring to structs within class fields

This is part 3 of Torquifying DescriptorArray: making it possible to use
the "descriptors" indexed field from code written in Torque. A small
macro EnsureArrayLengthWritable is converted to demonstrate the new
functionality.

This CL also introduces the arrow token `->` and desugars a->b to (*a).b
so that the new builtin looks a little cleaner.

Bug: v8:7793
Change-Id: I84eaa97f664aa67273866760e6ede4346a3ee2f9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1900332
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65046}
parent a8c28fa1
......@@ -52,4 +52,30 @@ namespace array {
}
extern macro SetPropertyLength(implicit context: Context)(JSAny, Number);
const kLengthDescriptorIndex:
constexpr int31 generates 'JSArray::kLengthDescriptorIndex';
const kAttributesReadOnlyMask: constexpr int31
generates 'PropertyDetails::kAttributesReadOnlyMask';
@export
macro EnsureArrayLengthWritable(implicit context: Context)(map: Map):
void labels Bailout {
// Don't support arrays in dictionary named property mode.
if (IsDictionaryMap(map)) {
goto Bailout;
}
// Check whether the length property is writable. The length property is the
// only default named property on arrays. It's nonconfigurable, hence is
// guaranteed to stay the first property.
const descriptors: DescriptorArray = map.instance_descriptors;
const descriptor:&DescriptorEntry =
& descriptors.descriptors[kLengthDescriptorIndex];
assert(TaggedEqual(descriptor->key, LengthStringConstant()));
const details: Smi = UnsafeCast<Smi>(descriptor->details);
if ((details & kAttributesReadOnlyMask) != 0) {
goto Bailout;
}
}
}
......@@ -520,9 +520,8 @@ extern macro ArrayCreate(implicit context: Context)(Number): JSArray;
extern macro BuildAppendJSArray(
constexpr ElementsKind, FastJSArray, JSAny): void labels Bailout;
extern macro EnsureArrayPushable(Map): ElementsKind
extern macro EnsureArrayPushable(implicit context: Context)(Map): ElementsKind
labels Bailout;
extern macro EnsureArrayLengthWritable(Map) labels Bailout;
// TODO: Reduce duplication once varargs are supported in macros.
extern macro Construct(implicit context: Context)(
Constructor, JSAny): JSReceiver;
......
......@@ -250,7 +250,7 @@ TF_BUILTIN(ArrayPrototypePop, CodeStubAssembler) {
GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &return_undefined);
// 2) Ensure that the length is writable.
EnsureArrayLengthWritable(LoadMap(array_receiver), &runtime);
EnsureArrayLengthWritable(context, LoadMap(array_receiver), &runtime);
// 3) Check that the elements backing store isn't copy-on-write.
TNode<FixedArrayBase> elements = LoadElements(array_receiver);
......@@ -339,7 +339,7 @@ TF_BUILTIN(ArrayPrototypePush, CodeStubAssembler) {
{
array_receiver = CAST(receiver);
arg_index = IntPtrConstant(0);
kind = EnsureArrayPushable(LoadMap(array_receiver), &runtime);
kind = EnsureArrayPushable(context, LoadMap(array_receiver), &runtime);
GotoIf(IsElementsKindGreaterThan(kind, HOLEY_SMI_ELEMENTS),
&object_push_pre);
......
......@@ -128,6 +128,9 @@ Convert<Smi, uint32>(ui: uint32): Smi {
Convert<uintptr, uint32>(ui: uint32): uintptr {
return ChangeUint32ToWord(ui);
}
Convert<intptr, uint16>(ui: uint16): intptr {
return Signed(ChangeUint32ToWord(ui));
}
Convert<int32, uint8>(i: uint8): int32 {
return Signed(Convert<uint32>(i));
}
......
......@@ -12,6 +12,9 @@ namespace torque_internal {
SizeOf<float64>(): constexpr int31 {
return kDoubleSize;
}
SizeOf<DescriptorEntry>(): constexpr int31 {
return kTaggedSize * 3;
}
// 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
......
......@@ -3013,37 +3013,15 @@ void CodeStubAssembler::StoreFeedbackVectorSlot(
}
}
void CodeStubAssembler::EnsureArrayLengthWritable(TNode<Map> map,
Label* bailout) {
// Don't support arrays in dictionary named property mode.
GotoIf(IsDictionaryMap(map), bailout);
// Check whether the length property is writable. The length property is the
// only default named property on arrays. It's nonconfigurable, hence is
// guaranteed to stay the first property.
TNode<DescriptorArray> descriptors = LoadMapDescriptors(map);
int length_index = JSArray::kLengthDescriptorIndex;
#ifdef DEBUG
TNode<Name> maybe_length =
LoadKeyByDescriptorEntry(descriptors, length_index);
CSA_ASSERT(this, TaggedEqual(maybe_length, LengthStringConstant()));
#endif
TNode<Uint32T> details =
LoadDetailsByDescriptorEntry(descriptors, length_index);
GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
bailout);
}
TNode<Int32T> CodeStubAssembler::EnsureArrayPushable(TNode<Map> map,
TNode<Int32T> CodeStubAssembler::EnsureArrayPushable(TNode<Context> context,
TNode<Map> map,
Label* bailout) {
// Disallow pushing onto prototypes. It might be the JSArray prototype.
// Disallow pushing onto non-extensible objects.
Comment("Disallow pushing onto prototypes");
GotoIfNot(IsExtensibleNonPrototypeMap(map), bailout);
EnsureArrayLengthWritable(map, bailout);
EnsureArrayLengthWritable(context, map, bailout);
TNode<Uint32T> kind =
DecodeWord32<Map::ElementsKindBits>(LoadMapBitField2(map));
......
......@@ -1684,14 +1684,13 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
int additional_offset = 0);
void EnsureArrayLengthWritable(TNode<Map> map, Label* bailout);
// EnsureArrayPushable verifies that receiver with this map is:
// 1. Is not a prototype.
// 2. Is not a dictionary.
// 3. Has a writeable length property.
// It returns ElementsKind as a node for further division into cases.
TNode<Int32T> EnsureArrayPushable(TNode<Map> map, Label* bailout);
TNode<Int32T> EnsureArrayPushable(TNode<Context> context, TNode<Map> map,
Label* bailout);
void TryStoreArrayElement(ElementsKind kind, ParameterMode mode,
Label* bailout, Node* elements, Node* index,
......
......@@ -9,6 +9,7 @@ extern class EnumCache extends Struct {
indices: FixedArray;
}
// Keep in sync with hand-coded SizeOf<DescriptorEntry> in torque-internal.tq.
@export
struct DescriptorEntry {
key: Name|Undefined;
......
......@@ -1834,6 +1834,40 @@ LocationReference ImplementationVisitor::GetLocationReference(
ProjectStructField(reference.temporary(), fieldname),
reference.temporary_description());
}
if (reference.IsHeapReference()) {
VisitResult ref = reference.heap_reference();
auto generic_type = StructType::MatchUnaryGeneric(
ref.type(), TypeOracle::GetReferenceGeneric());
if (!generic_type) {
ReportError(
"Left-hand side of field access expression is marked as a reference "
"but is not of type Reference<...>. Found type: ",
ref.type()->ToString());
}
if (const StructType* struct_type =
StructType::DynamicCast(*generic_type)) {
const Field& field = struct_type->LookupField(expr->field->value);
// Update the Reference's type to refer to the field type within the
// struct.
ref.SetType(TypeOracle::GetReferenceType(field.name_and_type.type));
if (field.offset != 0) {
// Copy the Reference struct up the stack and update the new copy's
// |offset| value to point to the struct field.
StackScope scope(this);
ref = GenerateCopy(ref);
VisitResult ref_offset = ProjectStructField(ref, "offset");
VisitResult struct_offset{
TypeOracle::GetIntPtrType()->ConstexprVersion(),
std::to_string(field.offset)};
VisitResult updated_offset =
GenerateCall("+", {{ref_offset, struct_offset}, {}});
assembler().Poke(ref_offset.stack_range(), updated_offset.stack_range(),
ref_offset.type());
ref = scope.Yield(ref);
}
return LocationReference::HeapReference(ref);
}
}
VisitResult object_result = GenerateFetchFromLocation(reference);
if (base::Optional<const ClassType*> class_type =
object_result.type()->ClassSupertype()) {
......
......@@ -1334,6 +1334,16 @@ base::Optional<ParseResult> MakeFieldAccessExpression(
return ParseResult{result};
}
base::Optional<ParseResult> MakeReferenceFieldAccessExpression(
ParseResultIterator* child_results) {
auto object = child_results->NextAs<Expression*>();
auto field = child_results->NextAs<Identifier*>();
// `a->b` is equivalent to `(*a).b`.
Expression* deref = MakeNode<DereferenceExpression>(object);
Expression* result = MakeNode<FieldAccessExpression>(deref, field);
return ParseResult{result};
}
base::Optional<ParseResult> MakeElementAccessExpression(
ParseResultIterator* child_results) {
auto object = child_results->NextAs<Expression*>();
......@@ -1834,6 +1844,8 @@ struct TorqueGrammar : Grammar {
Rule({&intrinsicCallExpression}),
Rule({&identifierExpression}),
Rule({&primaryExpression, Token("."), &name}, MakeFieldAccessExpression),
Rule({&primaryExpression, Token("->"), &name},
MakeReferenceFieldAccessExpression),
Rule({&primaryExpression, Token("["), expression, Token("]")},
MakeElementAccessExpression),
Rule({&decimalLiteral}, MakeNumberLiteralExpression),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment