[metagen] Derive Torque names of class-scoped enum bit field types
Record enum bit field types with their enclosing classes, e.g. JSPluralRules::Type, and name them in Torque by dropping the "::" (JSPluralRulesType), as for bit field group structs. Rename the Torque types that did not follow this rule: DescriptorArrayFastIterableState, NameHashFieldType, and PromisePromiseState. Also allow 'type' as a Torque identifier, for bit fields named after a TypeBit alias. Bug: 531344950 Change-Id: Iee8fb965945a416a98a4d256521bae896918ab63 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8657159 Reviewed-by: Igor Sheludko <ishell@chromium.org> Reviewed-by: Hannes Payer <hpayer@chromium.org> Commit-Queue: Hannes Payer <hpayer@chromium.org> Auto-Submit: Jakob Linke <jgruber@chromium.org> Cr-Commit-Position: refs/heads/main@{#111153}
This commit is contained in:
1 parent
0530bcbf67
commit
94fe2a753a
14 files changed
+142
-55
No files matched your search
@@ -345,11 +345,8 @@ extern enum CallFeedbackContent extends int32 { kTarget, kReceiver }
|
||||
extern enum UnicodeEncoding { UTF16, UTF32 }
|
||||
|
||||
// Promise constants
|
||||
extern enum PromiseState extends int31 constexpr 'Promise::PromiseState' {
|
||||
kPending,
|
||||
kFulfilled,
|
||||
kRejected
|
||||
}
|
||||
extern enum PromisePromiseState extends int31
|
||||
constexpr 'Promise::PromiseState' { kPending, kFulfilled, kRejected }
|
||||
|
||||
const kTaggedSize: constexpr int31 generates 'kTaggedSize';
|
||||
const kDoubleSize: constexpr int31 generates 'kDoubleSize';
|
||||
@@ -1338,7 +1335,7 @@ operator '+' macro StringAdd(
|
||||
}
|
||||
|
||||
operator '==' macro PromiseStateEquals(
|
||||
s1: PromiseState, s2: PromiseState): bool {
|
||||
s1: PromisePromiseState, s2: PromisePromiseState): bool {
|
||||
return Word32Equal(s1, s2);
|
||||
}
|
||||
|
||||
@@ -2267,7 +2264,8 @@ extern operator '[]' macro LoadWeakFixedArrayElement(
|
||||
extern operator '[]' macro LoadUint8Ptr(RawPtr<uint8>, intptr): uint8;
|
||||
extern operator '[]' macro LoadUint64Ptr(RawPtr<uint64>, intptr): uint64;
|
||||
|
||||
extern enum HashFieldType extends uint32 constexpr 'Name::HashFieldType' {
|
||||
extern enum NameHashFieldType extends uint32
|
||||
constexpr 'Name::HashFieldType' {
|
||||
kHash,
|
||||
kIntegerIndex,
|
||||
kForwardingIndex,
|
||||
@@ -2275,7 +2273,7 @@ extern enum HashFieldType extends uint32 constexpr 'Name::HashFieldType' {
|
||||
}
|
||||
|
||||
operator '==' macro HashFieldTypeEquals(
|
||||
s1: HashFieldType, s2: HashFieldType): bool {
|
||||
s1: NameHashFieldType, s2: NameHashFieldType): bool {
|
||||
return Word32Equal(s1, s2);
|
||||
}
|
||||
|
||||
|
||||
@@ -168,9 +168,9 @@ FromConstexpr<LanguageModeSmi, constexpr LanguageMode>(
|
||||
c: constexpr LanguageMode): LanguageModeSmi {
|
||||
return %RawDownCast<LanguageModeSmi>(SmiConstant(c));
|
||||
}
|
||||
FromConstexpr<PromiseState, constexpr PromiseState>(c: constexpr PromiseState):
|
||||
PromiseState {
|
||||
return %RawDownCast<PromiseState>(Int32Constant(c));
|
||||
FromConstexpr<PromisePromiseState, constexpr PromisePromiseState>(
|
||||
c: constexpr PromisePromiseState): PromisePromiseState {
|
||||
return %RawDownCast<PromisePromiseState>(Int32Constant(c));
|
||||
}
|
||||
FromConstexpr<InstanceType, constexpr InstanceType>(c: constexpr InstanceType):
|
||||
InstanceType {
|
||||
@@ -444,8 +444,8 @@ extern macro BIntToSmi(bint): Smi;
|
||||
Convert<Smi, bint>(v: bint): Smi {
|
||||
return BIntToSmi(v);
|
||||
}
|
||||
Convert<PromiseState, int32>(s: int32): PromiseState {
|
||||
return %RawDownCast<PromiseState>(s);
|
||||
Convert<PromisePromiseState, int32>(s: int32): PromisePromiseState {
|
||||
return %RawDownCast<PromisePromiseState>(s);
|
||||
}
|
||||
Convert<I8X16, Simd128>(s: Simd128): I8X16 {
|
||||
return %RawDownCast<I8X16>(s);
|
||||
|
||||
@@ -208,8 +208,8 @@ transitioning macro TriggerPromiseReactions(
|
||||
// https://tc39.es/ecma262/#sec-fulfillpromise
|
||||
transitioning builtin FulfillPromise(
|
||||
implicit context: Context)(promise: JSPromise, value: JSAny): Undefined {
|
||||
// Assert: The value of promise.[[PromiseState]] is "pending".
|
||||
dcheck(promise.Status() == PromiseState::kPending);
|
||||
// Assert: The value of promise.[[PromisePromiseState]] is "pending".
|
||||
dcheck(promise.Status() == PromisePromiseState::kPending);
|
||||
|
||||
RunContextPromiseHookResolve(promise);
|
||||
|
||||
@@ -222,8 +222,8 @@ transitioning builtin FulfillPromise(
|
||||
// 5. Set promise.[[PromiseRejectReactions]] to undefined.
|
||||
promise.reactions_or_result = value;
|
||||
|
||||
// 6. Set promise.[[PromiseState]] to "fulfilled".
|
||||
promise.SetStatus(PromiseState::kFulfilled);
|
||||
// 6. Set promise.[[PromisePromiseState]] to "fulfilled".
|
||||
promise.SetStatus(PromisePromiseState::kFulfilled);
|
||||
|
||||
// 7. Return TriggerPromiseReactions(reactions, value).
|
||||
TriggerPromiseReactions(reactions, value, kPromiseReactionFulfill);
|
||||
@@ -268,8 +268,8 @@ transitioning builtin RejectPromise(
|
||||
// 5. Set promise.[[PromiseRejectReactions]] to undefined.
|
||||
promise.reactions_or_result = reason;
|
||||
|
||||
// 6. Set promise.[[PromiseState]] to "rejected".
|
||||
promise.SetStatus(PromiseState::kRejected);
|
||||
// 6. Set promise.[[PromisePromiseState]] to "rejected".
|
||||
promise.SetStatus(PromisePromiseState::kRejected);
|
||||
|
||||
// 8. Return TriggerPromiseReactions(reactions, reason).
|
||||
TriggerPromiseReactions(reactions, reason, kPromiseReactionReject);
|
||||
@@ -470,7 +470,7 @@ transitioning macro PerformPromiseThenImpl(
|
||||
implicit context: Context)(promise: JSPromise,
|
||||
onFulfilled: Callable|Undefined, onRejected: Callable|Undefined,
|
||||
resultPromiseOrCapability: JSPromise|PromiseCapability|Undefined): void {
|
||||
if (promise.Status() == PromiseState::kPending) {
|
||||
if (promise.Status() == PromisePromiseState::kPending) {
|
||||
// The {promise} is still in "Pending" state, so we just record a new
|
||||
// PromiseReaction holding both the onFulfilled and onRejected callbacks.
|
||||
// Once the {promise} is resolved we decide on the concrete handler to
|
||||
@@ -485,14 +485,14 @@ transitioning macro PerformPromiseThenImpl(
|
||||
const reactionsOrResult = promise.reactions_or_result;
|
||||
let microtask: PromiseReactionJobTask;
|
||||
let handlerContext: Context;
|
||||
if (promise.Status() == PromiseState::kFulfilled) {
|
||||
if (promise.Status() == PromisePromiseState::kFulfilled) {
|
||||
handlerContext = ExtractHandlerContext(onFulfilled, onRejected);
|
||||
microtask = NewPromiseFulfillReactionJobTask(
|
||||
handlerContext, reactionsOrResult, onFulfilled,
|
||||
resultPromiseOrCapability);
|
||||
} else
|
||||
deferred {
|
||||
dcheck(promise.Status() == PromiseState::kRejected);
|
||||
dcheck(promise.Status() == PromisePromiseState::kRejected);
|
||||
handlerContext = ExtractHandlerContext(onRejected, onFulfilled);
|
||||
microtask = NewPromiseRejectReactionJobTask(
|
||||
handlerContext, reactionsOrResult, onRejected,
|
||||
@@ -542,7 +542,7 @@ transitioning javascript builtin PromiseReject(
|
||||
|
||||
const promiseFun = *NativeContextSlot(ContextSlot::PROMISE_FUNCTION_INDEX);
|
||||
if (promiseFun == receiver) {
|
||||
const promise = NewJSPromise(PromiseState::kRejected, reason);
|
||||
const promise = NewJSPromise(PromisePromiseState::kRejected, reason);
|
||||
runtime::PromiseRejectEventFromStack(promise, reason);
|
||||
return promise;
|
||||
} else {
|
||||
|
||||
@@ -48,7 +48,7 @@ macro PromiseHasHandler(promise: JSPromise): bool {
|
||||
macro PromiseInit(promise: JSPromise): void {
|
||||
promise.reactions_or_result = kZero;
|
||||
promise.flags = SmiTag(JSPromiseFlags{
|
||||
status: PromiseState::kPending,
|
||||
status: PromisePromiseState::kPending,
|
||||
is_native_resolver_invoked: false,
|
||||
has_handler: false,
|
||||
is_silent: false,
|
||||
@@ -68,7 +68,7 @@ macro InnerNewJSPromise(implicit context: Context)(): JSPromise {
|
||||
promise.elements = kEmptyFixedArray;
|
||||
promise.reactions_or_result = kZero;
|
||||
promise.flags = SmiTag(JSPromiseFlags{
|
||||
status: PromiseState::kPending,
|
||||
status: PromisePromiseState::kPending,
|
||||
is_native_resolver_invoked: false,
|
||||
has_handler: false,
|
||||
is_silent: false,
|
||||
@@ -288,9 +288,9 @@ transitioning macro NewJSPromise(implicit context: Context)(): JSPromise {
|
||||
// fields.
|
||||
@export
|
||||
transitioning macro NewJSPromise(
|
||||
implicit context: Context)(status: constexpr PromiseState,
|
||||
implicit context: Context)(status: constexpr PromisePromiseState,
|
||||
result: JSAny): JSPromise {
|
||||
dcheck(status != PromiseState::kPending);
|
||||
dcheck(status != PromisePromiseState::kPending);
|
||||
|
||||
const instance = InnerNewJSPromise();
|
||||
instance.reactions_or_result = result;
|
||||
|
||||
@@ -16,12 +16,12 @@ struct DescriptorEntry {
|
||||
NumberDictionary;
|
||||
}
|
||||
|
||||
type FastIterableState extends uint8
|
||||
type DescriptorArrayFastIterableState extends uint8
|
||||
constexpr 'DescriptorArray::FastIterableState';
|
||||
|
||||
@cppScope('DescriptorArray::Flags')
|
||||
bitfield struct DescriptorArrayFlags extends uint32 {
|
||||
fast_iterable: FastIterableState: 2 bit;
|
||||
fast_iterable: DescriptorArrayFastIterableState: 2 bit;
|
||||
}
|
||||
|
||||
@cppObjectLayoutDefinition
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
@cppScope('JSPromise::Flags')
|
||||
bitfield struct JSPromiseFlags extends uint31 {
|
||||
status: PromiseState: 2 bit;
|
||||
status: PromisePromiseState: 2 bit;
|
||||
is_native_resolver_invoked: bool: 1 bit;
|
||||
has_handler: bool: 1 bit;
|
||||
is_silent: bool: 1 bit;
|
||||
@@ -13,13 +13,13 @@ bitfield struct JSPromiseFlags extends uint31 {
|
||||
|
||||
@cppObjectLayoutDefinition
|
||||
extern class JSPromise extends JSObjectWithEmbedderSlots {
|
||||
macro Status(): PromiseState {
|
||||
macro Status(): PromisePromiseState {
|
||||
return this.flags.status;
|
||||
}
|
||||
|
||||
macro SetStatus(status: constexpr PromiseState): void {
|
||||
dcheck(this.Status() == PromiseState::kPending);
|
||||
dcheck(status != PromiseState::kPending);
|
||||
macro SetStatus(status: constexpr PromisePromiseState): void {
|
||||
dcheck(this.Status() == PromisePromiseState::kPending);
|
||||
dcheck(status != PromisePromiseState::kPending);
|
||||
|
||||
this.flags.status = status;
|
||||
}
|
||||
|
||||
+3
-3
@@ -10,7 +10,7 @@ extern class Name extends PrimitiveHeapObject {
|
||||
|
||||
@cppScope('Name::Hash')
|
||||
bitfield struct NameHash extends uint32 {
|
||||
hash_field_type: HashFieldType: 2 bit;
|
||||
hash_field_type: NameHashFieldType: 2 bit;
|
||||
|
||||
// hash_field_type: kHash
|
||||
hash: uint32: 30 bit at 2;
|
||||
@@ -64,7 +64,7 @@ const kArrayIndexValueBits: constexpr int31
|
||||
const kDoesNotContainCachedArrayIndexMask: constexpr uint32
|
||||
generates 'Name::kDoesNotContainCachedArrayIndexMask';
|
||||
const kNameEmptyHashField: NameHash =
|
||||
NameHash{hash_field_type: HashFieldType::kEmpty};
|
||||
NameHash{hash_field_type: NameHashFieldType::kEmpty};
|
||||
|
||||
macro ContainsCachedArrayIndex(hash: uint32): bool {
|
||||
return (hash & kDoesNotContainCachedArrayIndexMask) == 0;
|
||||
@@ -89,7 +89,7 @@ macro TenToThe(exponent: uint32): uint32 {
|
||||
}
|
||||
|
||||
macro IsIntegerIndex(hash: NameHash): bool {
|
||||
return hash.hash_field_type == HashFieldType::kIntegerIndex;
|
||||
return hash.hash_field_type == NameHashFieldType::kIntegerIndex;
|
||||
}
|
||||
|
||||
// This is in sync with the private StringHasher::MakeArrayIndexHash without
|
||||
|
||||
@@ -85,9 +85,8 @@ std::string TorqueClassNameFromCppName(const std::string& cpp_name,
|
||||
return cpp_name.substr(separator + 2);
|
||||
}
|
||||
|
||||
std::string TorqueBitFieldStructName(const JsonObject& storage,
|
||||
const std::string& context) {
|
||||
std::string name = RequireString(storage, "name", context);
|
||||
// Torque has no nested types: the C++ type Foo::Bar is FooBar in Torque.
|
||||
std::string TorqueNameFromClassScopedName(std::string name) {
|
||||
size_t pos;
|
||||
while ((pos = name.find("::")) != std::string::npos) {
|
||||
name.erase(pos, 2);
|
||||
@@ -95,6 +94,11 @@ std::string TorqueBitFieldStructName(const JsonObject& storage,
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string TorqueBitFieldStructName(const JsonObject& storage,
|
||||
const std::string& context) {
|
||||
return TorqueNameFromClassScopedName(RequireString(storage, "name", context));
|
||||
}
|
||||
|
||||
// Map primitive C++ types to their Torque representation. Torque already knows
|
||||
// all other class, struct, and namespace-scope alias names.
|
||||
struct PrimitiveTypeName {
|
||||
@@ -1344,7 +1348,8 @@ class LayoutImporter {
|
||||
NameAndTypeExpression{
|
||||
MakeNode<Identifier>(
|
||||
TorqueBitFieldName(cpp_name, num_bits, context)),
|
||||
NamedType(TorqueTypeName(RequireString(b, "type", context)))},
|
||||
NamedType(TorqueTypeName(TorqueNameFromClassScopedName(
|
||||
RequireString(b, "type", context))))},
|
||||
static_cast<int>(num_bits), static_cast<int>(offset)});
|
||||
}
|
||||
return fields;
|
||||
|
||||
@@ -2439,7 +2439,8 @@ struct TorqueGrammar : Grammar {
|
||||
|
||||
// Result: std::string
|
||||
Symbol identifier = {Rule({Pattern(MatchIdentifier)}, YieldMatchedInput),
|
||||
Rule({Token("runtime")}, YieldMatchedInput)};
|
||||
Rule({Token("runtime")}, YieldMatchedInput),
|
||||
Rule({Token("type")}, YieldMatchedInput)};
|
||||
|
||||
// Result: Identifier*
|
||||
Symbol name = {Rule({&identifier}, MakeIdentifier)};
|
||||
|
||||
@@ -1734,6 +1734,60 @@ TEST(TorqueLayoutLoader, VerifierRejectsBitFieldStructNameMismatch) {
|
||||
"the C++ bit field group is Test::BitFields, Torque uses OtherBits");
|
||||
}
|
||||
|
||||
// The class-scoped enum TestScalar::Mode, named in Torque by dropping the
|
||||
// "::".
|
||||
std::string TestModeEnum(const std::string& name = "TestScalarMode") {
|
||||
return "extern enum " + name +
|
||||
" extends uint8 constexpr 'TestScalar::Mode' { kA, kB }\n";
|
||||
}
|
||||
|
||||
// TestBitFieldStorage() with a bit of type TestScalar::Mode in place of bar.
|
||||
std::string TestModeBitFieldStorage() {
|
||||
return TestBitFieldStorage(
|
||||
R"({"cpp_name": "IsFooBit", "type": "bool", )"
|
||||
R"("offset": 0, "num_bits": 1}, )"
|
||||
R"({"cpp_name": "ModeBit", "type": "TestScalar::Mode", )"
|
||||
R"("offset": 1, "num_bits": 1})");
|
||||
}
|
||||
|
||||
TEST(TorqueLayoutLoader, ImporterDerivesClassScopedEnumBitFieldType) {
|
||||
ExpectSuccessfulLayoutCompilation(
|
||||
TestModeEnum() + TestScalarClass("TestBitFields"),
|
||||
TestScalarJson(TestModeBitFieldStorage(), 1),
|
||||
/*use_cpp_layouts=*/true);
|
||||
}
|
||||
|
||||
TEST(TorqueLayoutLoader, ImporterReplacesBitFieldStructWithClassScopedEnum) {
|
||||
ExpectSuccessfulLayoutCompilation(
|
||||
TestModeEnum() + TestBitFieldStruct("mode: TestScalarMode: 1 bit;") +
|
||||
TestScalarClass("TestBitFields"),
|
||||
TestScalarJson(TestModeBitFieldStorage(), 1),
|
||||
/*use_cpp_layouts=*/true);
|
||||
}
|
||||
|
||||
TEST(TorqueLayoutLoader, ImporterRejectsEnumNotNamedAfterCppType) {
|
||||
ExpectFailingLayoutCompilation(
|
||||
TestModeEnum("TestMode") + TestScalarClass("TestBitFields"),
|
||||
TestScalarJson(TestModeBitFieldStorage(), 1),
|
||||
/*use_cpp_layouts=*/true, "cannot find \"TestScalarMode\"");
|
||||
}
|
||||
|
||||
TEST(TorqueLayoutLoader, AcceptsBitFieldNamedType) {
|
||||
for (bool use_cpp_layouts : {true, false}) {
|
||||
ExpectSuccessfulLayoutCompilation(
|
||||
TestBitFieldStruct("type: uint8: 3 bit;") +
|
||||
TestScalarClass("TestBitFields"),
|
||||
TestScalarJson(
|
||||
TestBitFieldStorage(R"({"cpp_name": "IsFooBit", "type": "bool", )"
|
||||
R"("offset": 0, "num_bits": 1}, )"
|
||||
R"({"cpp_name": "TypeBits", )"
|
||||
R"("type": "uint8_t", )"
|
||||
R"("offset": 1, "num_bits": 3})"),
|
||||
1),
|
||||
use_cpp_layouts);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TorqueLayoutLoader, ImporterDeclaresBitFieldStructFromCppOnlyBase) {
|
||||
// TestBase has no Torque declaration. Import its fields into TestScalar,
|
||||
// including the bitfield struct used by value_.
|
||||
|
||||
@@ -148,12 +148,13 @@ def _bit_field_group_base(decl: cindex.Cursor) -> cindex.Type | None:
|
||||
return None
|
||||
|
||||
|
||||
def _cpp_bit_field_struct_name(decl: cindex.Cursor) -> str:
|
||||
"""The C++ name of a BitFieldGroup, including enclosing classes."""
|
||||
def _cpp_class_scoped_name(decl: cindex.Cursor) -> str:
|
||||
"""The C++ name of `decl`, qualified by its enclosing classes."""
|
||||
parts = []
|
||||
while decl is not None and decl.kind in (cindex.CursorKind.CLASS_DECL,
|
||||
cindex.CursorKind.STRUCT_DECL,
|
||||
cindex.CursorKind.CLASS_TEMPLATE):
|
||||
cindex.CursorKind.CLASS_TEMPLATE,
|
||||
cindex.CursorKind.ENUM_DECL):
|
||||
parts.append(decl.spelling)
|
||||
decl = decl.semantic_parent
|
||||
return "::".join(reversed(parts))
|
||||
@@ -375,7 +376,7 @@ class _Extractor:
|
||||
if canonical.kind == cindex.TypeKind.ENUM:
|
||||
decl = canonical.get_declaration()
|
||||
mark_visited(self.parsed, decl)
|
||||
return decl.spelling
|
||||
return _cpp_class_scoped_name(decl)
|
||||
if canonical.kind == cindex.TypeKind.RECORD:
|
||||
decl = _decl_of(canonical)
|
||||
if decl is not None and decl.spelling == "StrongAlias":
|
||||
@@ -481,8 +482,8 @@ class _Extractor:
|
||||
base = _bit_field_group_base(decl)
|
||||
if base is not None:
|
||||
mark_visited(self.parsed, decl)
|
||||
return self._bitfield_storage(field, _cpp_bit_field_struct_name(decl),
|
||||
decl, base)
|
||||
return self._bitfield_storage(field, _cpp_class_scoped_name(decl), decl,
|
||||
base)
|
||||
|
||||
if decl is not None and decl.kind in (cindex.CursorKind.CLASS_DECL,
|
||||
cindex.CursorKind.STRUCT_DECL):
|
||||
@@ -520,7 +521,7 @@ class _Extractor:
|
||||
f"is not a BitFieldGroup")
|
||||
mark_visited(self.parsed, group)
|
||||
return self._bitfield_storage(
|
||||
field, _cpp_bit_field_struct_name(group), group, base, smi=True)
|
||||
field, _cpp_class_scoped_name(group), group, base, smi=True)
|
||||
if name == "TrustedPointerMember":
|
||||
args = _template_args(elem_type) or _template_args(canonical)
|
||||
if not args:
|
||||
|
||||
@@ -72,6 +72,12 @@ def _check_identifier(s: str, what: str) -> None:
|
||||
f"{what}: not an identifier: {s!r}")
|
||||
|
||||
|
||||
def _check_class_scoped_name(s: str, what: str) -> None:
|
||||
_check(isinstance(s, str), f"{what} must be a string")
|
||||
for part in s.split("::"):
|
||||
_check_identifier(part, what)
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Config:
|
||||
"""The build configuration the layouts were extracted under.
|
||||
@@ -268,7 +274,7 @@ class BitFieldEntry:
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
_check_identifier(self.cpp_name, "bit field name")
|
||||
_check_identifier(self.type, "bit field type")
|
||||
_check_class_scoped_name(self.type, "bit field type")
|
||||
_check(self.num_bits > 0, f"bit field {self.cpp_name}: no bits")
|
||||
_check(self.offset >= 0, f"bit field {self.cpp_name}: negative offset")
|
||||
|
||||
@@ -333,11 +339,7 @@ class StorageType:
|
||||
_check(self.width is not None and self.width > 0,
|
||||
"struct storage needs a size")
|
||||
if self.kind in ("bitfield", "smi_bitfield"):
|
||||
_check(
|
||||
isinstance(self.name, str),
|
||||
f"{self.kind} storage name must be a string")
|
||||
for part in (self.name or "").split("::"):
|
||||
_check_identifier(part, f"{self.kind} storage name")
|
||||
_check_class_scoped_name(self.name, f"{self.kind} storage name")
|
||||
if self.kind == "bitfield":
|
||||
_check(self.width in (1, 2, 4, 8), "bitfield storage width invalid")
|
||||
capacity = self.width * 8
|
||||
|
||||
@@ -776,6 +776,23 @@ class BitFieldGroupTest(unittest.TestCase):
|
||||
""")["Foo"].fields
|
||||
self.assertEqual(field.storage.name, "Foo::Holder::Bits")
|
||||
|
||||
def test_class_scoped_enum_bit_field_type_is_qualified(self):
|
||||
(field,) = self._layouts("""
|
||||
namespace v8::internal {
|
||||
class Foo : public HeapObject {
|
||||
public:
|
||||
enum class Mode : uint8_t { kA, kB };
|
||||
struct Bits : BitFieldGroup<Bits, uint32_t> {
|
||||
using ModeBits = First<Mode, 1>;
|
||||
using KindBits = ModeBits::Next<Kind, 2>;
|
||||
};
|
||||
Bits bits_;
|
||||
};
|
||||
}
|
||||
""")["Foo"].fields
|
||||
self.assertEqual([(b.cpp_name, b.type) for b in field.storage.bits],
|
||||
[("ModeBits", "Foo::Mode"), ("KindBits", "Kind")])
|
||||
|
||||
def test_smi_member_records_the_group_as_a_tagged_slot(self):
|
||||
(field,) = self._layouts("""
|
||||
class Foo : public HeapObject {
|
||||
|
||||
@@ -150,6 +150,15 @@ class StorageTypeTest(unittest.TestCase):
|
||||
width=1,
|
||||
bits=(BitFieldEntry("IsFooBit", "bool", 0, 1),))
|
||||
|
||||
def test_bitfield_type_may_be_class_scoped(self):
|
||||
s = bitfield(BitFieldEntry("ModeBits", "Foo::Mode", 0, 2))
|
||||
self.assertEqual(StorageType.from_json(s.to_json()), s)
|
||||
|
||||
def test_bitfield_rejects_invalid_qualified_types(self):
|
||||
for type_name in (None, 42, "::Mode", "Foo::", "Foo::::Mode", "Foo:Mode"):
|
||||
with self.subTest(type_name=type_name), self.assertRaises(LayoutError):
|
||||
BitFieldEntry("ModeBits", type_name, 0, 2)
|
||||
|
||||
def test_bitfield_needs_fields(self):
|
||||
with self.assertRaisesRegex(LayoutError, "without fields"):
|
||||
bitfield()
|
||||
|
||||
Reference in new issue
Block a user