Commit 4d05884e authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[torque] Introduce @abstract annotation for Torque classes

This annotation indicates that the class itself is not instantiated,
and does not have its own instance type: The instance types that
logically belong to the class are the instance types of the derived
classes.

Currently, we need the indication @dirtyInstantiatedAbstractClass
for several classes that are used as both, abstract base classes
and concrete classes. The prime example is JSObject which is the
base for many other classes, and also serves as the class to allocate
plain JSObjects. The annotation is purposefully ugly because in the
future we should refactor code to make it unnecessary.

Another annotation we introduce is @hasSameInstanceTypeAsParent,
which indicates another design pattern that currently occurs in the
code-base: Some Torque classes have the same instance types as their
parent class, but rename some fields, or possibly have a different map.
In such cases, the parent class is not abstract and the derived classes
can be seen as refinements of this class (that, for example, narrows the
type of a field). In the future, Torque should accomodate this pattern
better, but at moment we are content with just indicating where it is
used.

Bug: v8:7793
Change-Id: I1892dcc7325250df75d80308bf3d767d6d43bcc2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1607761
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61495}
parent 617b990f
......@@ -30,7 +30,10 @@ type PositiveSmi extends Smi;
// The Smi value zero, which is often used as null for HeapObject types.
type Zero extends PositiveSmi;
extern class HeapObject extends Tagged { map: Map; }
@abstract
extern class HeapObject extends Tagged {
map: Map;
}
type Object = Smi | HeapObject;
type int32 generates 'TNode<Int32T>' constexpr 'int32_t';
......@@ -78,6 +81,7 @@ type Number = Smi | HeapNumber;
type BigInt extends HeapObject generates 'TNode<BigInt>';
type Numeric = Number | BigInt;
@abstract
@noVerifier
extern class Name extends HeapObject {
hash_field: int32;
......@@ -88,14 +92,17 @@ extern class Symbol extends Name {
name: Object;
}
// abstract
extern class String extends Name { length: uint32; }
@abstract
extern class String extends Name {
length: uint32;
}
extern class ConsString extends String {
first: String;
second: String;
}
@abstract
@noVerifier
extern class ExternalString extends String {
resource: RawPtr;
......@@ -108,6 +115,7 @@ extern class ExternalTwoByteString extends ExternalString {}
extern class InternalizedString extends String {}
// TODO(v8:8983): Add declaration for variable-sized region.
@abstract
@noVerifier
extern class SeqString extends String {
}
......@@ -124,16 +132,21 @@ extern class ThinString extends String { actual: String; }
// The HeapNumber value NaN
type NaN extends HeapNumber;
@abstract
@noVerifier
extern class Struct extends HeapObject {
}
@abstract
@dirtyInstantiatedAbstractClass
@generatePrint
extern class Tuple2 extends Struct {
value1: Object;
value2: Object;
}
@abstract
@dirtyInstantiatedAbstractClass
@generatePrint
extern class Tuple3 extends Tuple2 {
value3: Object;
......@@ -147,6 +160,7 @@ type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
type Map extends HeapObject generates 'TNode<Map>';
@abstract
@noVerifier
extern class FixedArrayBase extends HeapObject {
length: Smi;
......@@ -191,6 +205,7 @@ intrinsic %GetAllocationBaseSize<Class: type>(map: Map): intptr;
intrinsic %Allocate<Class: type>(size: intptr): Class;
intrinsic %AllocateInternalClass<Class: type>(slotCount: constexpr intptr): Class;
@abstract
@noVerifier
extern class JSReceiver extends HeapObject {
properties_or_hash: FixedArrayBase | Smi;
......@@ -198,6 +213,8 @@ extern class JSReceiver extends HeapObject {
type Constructor extends JSReceiver;
@abstract
@dirtyInstantiatedAbstractClass
extern class JSObject extends JSReceiver {
@noVerifier elements: FixedArrayBase;
}
......@@ -254,9 +271,11 @@ extern class JSValue extends JSObject { value: Object; }
extern class JSArgumentsObject extends JSObject {}
@noVerifier
@hasSameInstanceTypeAsParent
extern class JSArgumentsObjectWithLength extends JSArgumentsObject {
length: Object;
}
@hasSameInstanceTypeAsParent
extern class JSSloppyArgumentsObject extends JSArgumentsObjectWithLength {
callee: Object;
}
......@@ -327,6 +346,7 @@ type NoSharedNameSentinel extends Smi;
type JSModuleNamespace extends JSObject;
type WeakArrayList extends HeapObject;
@abstract
@noVerifier
extern class JSWeakCollection extends JSObject {
table: Object;
......@@ -431,7 +451,7 @@ extern operator '.length_intptr' macro LoadAndUntagFixedArrayBaseLength(
type FixedTypedArray extends FixedTypedArrayBase
generates 'TNode<FixedTypedArray>';
extern class SloppyArgumentsElements extends FixedArray {}
type SloppyArgumentsElements extends FixedArray;
type NumberDictionary extends HeapObject
generates 'TNode<NumberDictionary>';
......@@ -491,6 +511,7 @@ extern class JSArrayBuffer extends JSObject {
backing_store: RawPtr;
}
@abstract
extern class JSArrayBufferView extends JSObject {
buffer: JSArrayBuffer;
byte_offset: uintptr;
......@@ -537,6 +558,7 @@ extern class JSAccessorPropertyDescriptor extends JSObject {
configurable: Object;
}
@abstract
@noVerifier
extern class JSCollection extends JSObject {
table: Object;
......@@ -579,6 +601,7 @@ extern class JSDataPropertyDescriptor extends JSObject {
configurable: Object;
}
@abstract
extern class TemplateInfo extends Struct {
tag: Object;
serial_number: Object;
......@@ -616,7 +639,7 @@ extern class ObjectTemplateInfo extends TemplateInfo {
extern class PropertyArray extends HeapObject { length_and_hash: Smi; }
extern class DependentCode extends WeakFixedArray {}
type DependentCode extends WeakFixedArray;
extern class PropertyCell extends HeapObject {
name: Name;
......@@ -686,6 +709,8 @@ extern class DataHandler extends Struct {
@noVerifier weak data_3: Object;
}
@abstract
@dirtyInstantiatedAbstractClass
extern class JSGeneratorObject extends JSObject {
function: JSFunction;
context: Context;
......@@ -710,7 +735,9 @@ extern class JSPromise extends JSObject {
flags: Smi;
}
extern class Microtask extends Struct {}
@abstract
extern class Microtask extends Struct {
}
extern class CallbackTask extends Microtask {
callback: Foreign;
......@@ -1005,6 +1032,7 @@ extern class PromiseReaction extends Struct {
promise_or_capability: JSPromise | PromiseCapability | Undefined;
}
@abstract
extern class PromiseReactionJobTask extends Microtask {
argument: Object;
context: Context;
......@@ -1061,6 +1089,7 @@ RegExpBuiltinsAssembler::FastLoadLastIndex(FastJSRegExp): Smi;
extern operator '.lastIndex=' macro
RegExpBuiltinsAssembler::FastStoreLastIndex(FastJSRegExp, Smi): void;
@hasSameInstanceTypeAsParent
extern class JSRegExpResult extends JSArray {
index: Object;
input: Object;
......@@ -1082,6 +1111,7 @@ macro GetStartOfCaptureIndex(captureIndex: constexpr int31): constexpr int31 {
return kRegExpMatchInfoFirstCaptureIndex + (captureIndex * 2);
}
@hasSameInstanceTypeAsParent
extern class RegExpMatchInfo extends FixedArray {
GetStartOfCapture(implicit context: Context)(captureIndex: constexpr int31):
Smi {
......@@ -1115,7 +1145,7 @@ extern class AccessorPair extends Struct {
extern class BreakPoint extends Tuple2 {}
extern class BreakPointInfo extends Tuple2 {}
extern class CoverageInfo extends FixedArray {}
type CoverageInfo extends FixedArray;
extern class DebugInfo extends Struct {
shared_function_info: SharedFunctionInfo;
......@@ -1187,9 +1217,7 @@ extern class WasmExceptionObject extends JSObject {
extern class WasmExceptionPackage extends JSReceiver {
}
@noVerifier
extern class WasmExportedFunction extends JSFunction {
}
type WasmExportedFunction extends JSFunction;
extern class AsmWasmData extends Struct {
managed_native_module: Foreign; // Managed<wasm::NativeModule>
......
......@@ -516,7 +516,8 @@ Node* PromiseBuiltinsAssembler::AllocatePromiseReaction(
Node* PromiseBuiltinsAssembler::AllocatePromiseReactionJobTask(
Node* map, Node* context, Node* argument, Node* handler,
Node* promise_or_capability) {
Node* const microtask = Allocate(PromiseReactionJobTask::kSize);
Node* const microtask =
Allocate(PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks);
StoreMapNoWriteBarrier(microtask, map);
StoreObjectFieldNoWriteBarrier(
microtask, PromiseReactionJobTask::kArgumentOffset, argument);
......@@ -640,8 +641,10 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions(
// Morph {current} from a PromiseReaction into a PromiseReactionJobTask
// and schedule that on the microtask queue. We try to minimize the number
// of stores here to avoid screwing up the store buffer.
STATIC_ASSERT(static_cast<int>(PromiseReaction::kSize) ==
static_cast<int>(PromiseReactionJobTask::kSize));
STATIC_ASSERT(
static_cast<int>(PromiseReaction::kSize) ==
static_cast<int>(
PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks));
if (type == PromiseReaction::kFulfill) {
StoreMapNoWriteBarrier(current,
RootIndex::kPromiseFulfillReactionJobTaskMap);
......
......@@ -553,7 +553,7 @@ class PrototypeInfo::BodyDescriptor final : public BodyDescriptorBase {
class JSWeakCollection::BodyDescriptorImpl final : public BodyDescriptorBase {
public:
STATIC_ASSERT(kTableOffset + kTaggedSize == kSize);
STATIC_ASSERT(kTableOffset + kTaggedSize == kSizeOfAllWeakCollections);
static bool IsValidSlot(Map map, HeapObject obj, int offset) {
return IsValidJSObjectSlotImpl(map, obj, offset);
......
......@@ -866,7 +866,7 @@ void JSArgumentsObject::JSArgumentsObjectVerify(Isolate* isolate) {
void SloppyArgumentsElements::SloppyArgumentsElementsVerify(Isolate* isolate,
JSObject holder) {
TorqueGeneratedClassVerifiers::SloppyArgumentsElementsVerify(*this, isolate);
FixedArrayVerify(isolate);
// Abort verification if only partially initialized (can't use arguments()
// getter because it does FixedArray::cast()).
if (get(kArgumentsIndex)->IsUndefined(isolate)) return;
......
......@@ -6122,8 +6122,10 @@ Handle<Object> JSPromise::TriggerPromiseReactions(Isolate* isolate,
}
if (handler_context.is_null()) handler_context = isolate->native_context();
STATIC_ASSERT(static_cast<int>(PromiseReaction::kSize) ==
static_cast<int>(PromiseReactionJobTask::kSize));
STATIC_ASSERT(
static_cast<int>(PromiseReaction::kSize) ==
static_cast<int>(
PromiseReactionJobTask::kSizeOfAllPromiseReactionJobTasks));
if (type == PromiseReaction::kFulfill) {
task->synchronized_set_map(
ReadOnlyRoots(isolate).promise_fulfill_reaction_job_task_map());
......
......@@ -100,8 +100,6 @@ class FixedArrayBase : public HeapObject {
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
TORQUE_GENERATED_FIXED_ARRAY_BASE_FIELDS)
static const int kHeaderSize = kSize;
protected:
// Special-purpose constructor for subclasses that have fast paths where
// their ptr() is a Smi.
......
......@@ -44,6 +44,8 @@ class JSSet : public JSCollection {
// Dispatched behavior.
DECL_PRINTER(JSSet)
DECL_VERIFIER(JSSet)
DEFINE_FIELD_OFFSET_CONSTANTS(JSCollection::kHeaderSize,
TORQUE_GENERATED_JSWEAK_SET_FIELDS)
OBJECT_CONSTRUCTORS(JSSet, JSCollection);
};
......@@ -72,6 +74,8 @@ class JSMap : public JSCollection {
// Dispatched behavior.
DECL_PRINTER(JSMap)
DECL_VERIFIER(JSMap)
DEFINE_FIELD_OFFSET_CONSTANTS(JSCollection::kHeaderSize,
TORQUE_GENERATED_JSWEAK_MAP_FIELDS)
OBJECT_CONSTRUCTORS(JSMap, JSCollection);
};
......@@ -121,6 +125,8 @@ class JSWeakCollection : public JSObject {
// Visit the whole object.
using BodyDescriptor = BodyDescriptorImpl;
static const int kSizeOfAllWeakCollections = kHeaderSize;
OBJECT_CONSTRUCTORS(JSWeakCollection, JSObject);
};
......@@ -133,6 +139,9 @@ class JSWeakMap : public JSWeakCollection {
DECL_PRINTER(JSWeakMap)
DECL_VERIFIER(JSWeakMap)
DEFINE_FIELD_OFFSET_CONSTANTS(JSWeakCollection::kHeaderSize,
TORQUE_GENERATED_JSWEAK_MAP_FIELDS)
STATIC_ASSERT(kSize == kSizeOfAllWeakCollections);
OBJECT_CONSTRUCTORS(JSWeakMap, JSWeakCollection);
};
......@@ -144,6 +153,9 @@ class JSWeakSet : public JSWeakCollection {
// Dispatched behavior.
DECL_PRINTER(JSWeakSet)
DECL_VERIFIER(JSWeakSet)
DEFINE_FIELD_OFFSET_CONSTANTS(JSWeakCollection::kHeaderSize,
TORQUE_GENERATED_JSWEAK_SET_FIELDS)
STATIC_ASSERT(kSize == kSizeOfAllWeakCollections);
OBJECT_CONSTRUCTORS(JSWeakSet, JSWeakCollection);
};
......
......@@ -266,8 +266,6 @@ class JSReceiver : public HeapObject {
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
TORQUE_GENERATED_JSRECEIVER_FIELDS)
static const int kHeaderSize = kSize;
bool HasProxyInPrototype(Isolate* isolate);
bool HasComplexElements();
......
......@@ -71,8 +71,6 @@ class Name : public HeapObject {
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
TORQUE_GENERATED_NAME_FIELDS)
static const int kHeaderSize = kSize;
// Mask constant for checking if a name has a computed hash code
// and if it is a string that is an array index. The least significant bit
// indicates whether a hash code has been computed. If the hash code has
......
......@@ -39,7 +39,7 @@ class PromiseReactionJobTask : public Microtask {
// Dispatched behavior.
DECL_CAST(PromiseReactionJobTask)
DECL_VERIFIER(PromiseReactionJobTask)
static const int kSizeOfAllPromiseReactionJobTasks = kHeaderSize;
OBJECT_CONSTRUCTORS(PromiseReactionJobTask, Microtask);
};
......@@ -51,6 +51,11 @@ class PromiseFulfillReactionJobTask : public PromiseReactionJobTask {
DECL_PRINTER(PromiseFulfillReactionJobTask)
DECL_VERIFIER(PromiseFulfillReactionJobTask)
DEFINE_FIELD_OFFSET_CONSTANTS(
PromiseReactionJobTask::kHeaderSize,
TORQUE_GENERATED_PROMISE_FULFILL_REACTION_JOB_TASK_FIELDS)
STATIC_ASSERT(kSize == kSizeOfAllPromiseReactionJobTasks);
OBJECT_CONSTRUCTORS(PromiseFulfillReactionJobTask, PromiseReactionJobTask);
};
......@@ -62,6 +67,11 @@ class PromiseRejectReactionJobTask : public PromiseReactionJobTask {
DECL_PRINTER(PromiseRejectReactionJobTask)
DECL_VERIFIER(PromiseRejectReactionJobTask)
DEFINE_FIELD_OFFSET_CONSTANTS(
PromiseReactionJobTask::kHeaderSize,
TORQUE_GENERATED_PROMISE_REJECT_REACTION_JOB_TASK_FIELDS)
STATIC_ASSERT(kSize == kSizeOfAllPromiseReactionJobTasks);
OBJECT_CONSTRUCTORS(PromiseRejectReactionJobTask, PromiseReactionJobTask);
};
......
......@@ -164,7 +164,7 @@ bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
// strings in generated code, we need to bailout to runtime.
Map new_map;
ReadOnlyRoots roots(heap);
if (size < ExternalString::kSize) {
if (size < ExternalString::kSizeOfAllExternalStrings) {
if (is_internalized) {
new_map = roots.uncached_external_internalized_string_map();
} else {
......@@ -238,7 +238,7 @@ bool String::MakeExternal(v8::String::ExternalOneByteStringResource* resource) {
// strings in generated code, we need to bailout to runtime.
Map new_map;
ReadOnlyRoots roots(heap);
if (size < ExternalString::kSize) {
if (size < ExternalString::kSizeOfAllExternalStrings) {
new_map = is_internalized
? roots.uncached_external_one_byte_internalized_string_map()
: roots.uncached_external_one_byte_string_map();
......
......@@ -341,8 +341,6 @@ class String : public Name {
DEFINE_FIELD_OFFSET_CONSTANTS(Name::kHeaderSize,
TORQUE_GENERATED_STRING_FIELDS)
static const int kHeaderSize = kSize;
// Max char codes.
static const int32_t kMaxOneByteCharCode = unibrow::Latin1::kMaxChar;
static const uint32_t kMaxOneByteCharCodeU = unibrow::Latin1::kMaxChar;
......@@ -698,6 +696,7 @@ class ExternalString : public String {
inline void DisposeResource();
STATIC_ASSERT(kResourceOffset == Internals::kStringResourceOffset);
static const int kSizeOfAllExternalStrings = kHeaderSize;
OBJECT_CONSTRUCTORS(ExternalString, String);
};
......@@ -734,6 +733,12 @@ class ExternalOneByteString : public ExternalString {
class BodyDescriptor;
DEFINE_FIELD_OFFSET_CONSTANTS(
ExternalString::kHeaderSize,
TORQUE_GENERATED_EXTERNAL_ONE_BYTE_STRING_FIELDS)
STATIC_ASSERT(kSize == kSizeOfAllExternalStrings);
OBJECT_CONSTRUCTORS(ExternalOneByteString, ExternalString);
};
......@@ -772,6 +777,12 @@ class ExternalTwoByteString : public ExternalString {
class BodyDescriptor;
DEFINE_FIELD_OFFSET_CONSTANTS(
ExternalString::kHeaderSize,
TORQUE_GENERATED_EXTERNAL_TWO_BYTE_STRING_FIELDS)
STATIC_ASSERT(kSize == kSizeOfAllExternalStrings);
OBJECT_CONSTRUCTORS(ExternalTwoByteString, ExternalString);
};
......
......@@ -169,7 +169,7 @@ class FunctionTemplateInfo : public TemplateInfo {
static const int kInvalidSerialNumber = 0;
DEFINE_FIELD_OFFSET_CONSTANTS(TemplateInfo::kSize,
DEFINE_FIELD_OFFSET_CONSTANTS(TemplateInfo::kHeaderSize,
TORQUE_GENERATED_FUNCTION_TEMPLATE_INFO_FIELDS)
static Handle<SharedFunctionInfo> GetOrCreateSharedFunctionInfo(
......@@ -221,7 +221,7 @@ class ObjectTemplateInfo : public TemplateInfo {
DECL_VERIFIER(ObjectTemplateInfo)
// Layout description.
DEFINE_FIELD_OFFSET_CONSTANTS(TemplateInfo::kSize,
DEFINE_FIELD_OFFSET_CONSTANTS(TemplateInfo::kHeaderSize,
TORQUE_GENERATED_OBJECT_TEMPLATE_INFO_FIELDS)
// Starting from given object template's constructor walk up the inheritance
......
......@@ -67,7 +67,10 @@ enum class ClassFlag {
kGeneratePrint = 1 << 1,
kGenerateVerify = 1 << 2,
kTransient = 1 << 3,
kHasIndexedField = 1 << 4
kAbstract = 1 << 4,
kInstantiatedAbstractClass = 1 << 5,
kHasSameInstanceTypeAsParent = 1 << 6,
kHasIndexedField = 1 << 7
};
using ClassFlags = base::Flags<ClassFlag>;
......
......@@ -164,13 +164,15 @@ void ImplementationVisitor::Visit(TypeAlias* alias) {
// TODO(danno): This is a pretty cheesy hack for now. There should be a more
// robust mechanism for this, e.g. declaring classes 'extern' or something.
if (class_type->nspace()->IsTestNamespace()) {
std::string class_name{
class_type->GetSuperClass()->GetGeneratedTNodeTypeName()};
const ClassType* super = class_type->GetSuperClass();
std::string class_name{super->GetGeneratedTNodeTypeName()};
header_out() << " class " << class_type->name() << " : public "
<< class_name << " {\n";
header_out() << " public:\n";
header_out() << " DEFINE_FIELD_OFFSET_CONSTANTS(" << class_name
<< "::kSize, TORQUE_GENERATED_"
<< "::";
header_out() << (super->IsAbstract() ? "kHeaderSize" : "kSize");
header_out() << ", TORQUE_GENERATED_"
<< CapifyStringWithUnderscores(class_type->name())
<< "_FIELDS)\n";
header_out() << " };\n";
......@@ -2954,7 +2956,6 @@ void ImplementationVisitor::GenerateClassFieldOffsets(
new_contents_stream << "V(k" << CamelifyString(f.name_and_type.name)
<< "Offset, " << size_string << ") \\\n";
}
ProcessFieldInSection(&section, &completed_sections,
FieldSectionType::kNoSection, &new_contents_stream);
CompleteFieldSection(&section, &completed_sections,
......@@ -2964,7 +2965,12 @@ void ImplementationVisitor::GenerateClassFieldOffsets(
FieldSectionType::kStrongSection,
&new_contents_stream);
new_contents_stream << "V(kSize, 0) \\\n";
if (type->IsAbstract()) {
new_contents_stream << "V(kHeaderSize, 0) \\\n";
}
if (!type->IsAbstract() || type->IsInstantiatedAbstractClass()) {
new_contents_stream << "V(kSize, 0) \\\n";
}
new_contents_stream << "\n";
}
}
......
......@@ -656,13 +656,24 @@ class AnnotationSet {
base::Optional<ParseResult> MakeClassDeclaration(
ParseResultIterator* child_results) {
AnnotationSet annotations(child_results,
{"@generatePrint", "@noVerifier"});
AnnotationSet annotations(
child_results,
{"@generatePrint", "@noVerifier", "@abstract",
"@dirtyInstantiatedAbstractClass", "@hasSameInstanceTypeAsParent"});
ClassFlags flags = ClassFlag::kNone;
bool generate_print = annotations.Contains("@generatePrint");
if (generate_print) flags |= ClassFlag::kGeneratePrint;
bool generate_verify = !annotations.Contains("@noVerifier");
if (generate_verify) flags |= ClassFlag::kGenerateVerify;
if (annotations.Contains("@abstract")) {
flags |= ClassFlag::kAbstract;
}
if (annotations.Contains("@dirtyInstantiatedAbstractClass")) {
flags |= ClassFlag::kInstantiatedAbstractClass;
}
if (annotations.Contains("@hasSameInstanceTypeAsParent")) {
flags |= ClassFlag::kHasSameInstanceTypeAsParent;
}
auto is_extern = child_results->NextAs<bool>();
if (is_extern) flags |= ClassFlag::kExtern;
auto transient = child_results->NextAs<bool>();
......
......@@ -329,7 +329,8 @@ std::string ClassType::ToExplicitString() const {
}
bool ClassType::AllowInstantiation() const {
return !IsExtern() || nspace()->IsDefaultNamespace();
return (!IsExtern() || nspace()->IsDefaultNamespace()) &&
(!IsAbstract() || IsInstantiatedAbstractClass());
}
void ClassType::Finalize() const {
......@@ -339,6 +340,14 @@ void ClassType::Finalize() const {
if (parent()) {
if (const ClassType* super_class = ClassType::DynamicCast(parent())) {
if (super_class->HasIndexedField()) flags_ |= ClassFlag::kHasIndexedField;
if (!super_class->IsAbstract() && !HasSameInstanceTypeAsParent()) {
ReportLintError(
"Super class must either be abstract (annotate super class with "
"@abstract) "
"or this class must have the same instance type as the super class "
"(annotate this class with @hasSameInstanceTypeAsParent).",
this->decl_->name->pos);
}
}
}
TypeVisitor::VisitClassFieldsAndMethods(const_cast<ClassType*>(this),
......
......@@ -517,6 +517,13 @@ class ClassType final : public AggregateType {
return flags_ & ClassFlag::kGenerateVerify;
}
bool IsTransient() const override { return flags_ & ClassFlag::kTransient; }
bool IsAbstract() const { return flags_ & ClassFlag::kAbstract; }
bool IsInstantiatedAbstractClass() const {
return flags_ & ClassFlag::kInstantiatedAbstractClass;
}
bool HasSameInstanceTypeAsParent() const {
return flags_ & ClassFlag::kHasSameInstanceTypeAsParent;
}
bool HasIndexedField() const override;
size_t size() const { return size_; }
const ClassType* GetSuperClass() const {
......
......@@ -896,6 +896,7 @@ namespace test {
type Baztype = Foo | FooType;
@abstract
@noVerifier
extern class Foo extends JSObject {
fooField: FooType;
......
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