[torque] Automatically generate verifier functions

This change generates functions that verify the things that Torque knows
about objects and their fields. We still must implement each verifier
function in objects-debug.cc, but we can call into the generated code to
verify that field types match their Torque definitions. If no additional
verification is required, we can use the macro USE_TORQUE_VERIFIER as a
shorthand for a verifier that calls the corresponding generated
function.

A new annotation @noVerifier can be applied to both class and field
definitions, to prevent generating verification code. This allows fully
customized verification for complicated cases like
JSFunction::prototype_or_initial_map, which might not exist at all, and
JSObject::elements, which might be a one pointer filler map.

Because Factory::InitializeJSObjectFromMap fills new objects with
undefined values, and many verifiers need to deal with partially-
initialized objects, the generated verifiers allow undefined values on
every class deriving from JSObject. In cases where stricter checks were
previously performed, they are kept in objects-debug.cc.

Bug: v8:7793
Change-Id: I84034efadca89ba0aceddf92e886ffbfaa4c23fa
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1594042
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61422}
This commit is contained in:
Seth Brenith 2019-05-10 09:37:48 -07:00 committed by Commit Bot
parent 41bc1cfd80
commit e483fb2731
16 changed files with 443 additions and 549 deletions

View File

@ -1053,6 +1053,8 @@ action("run_torque") {
outputs = [
"$target_gen_dir/torque-generated/builtin-definitions-from-dsl.h",
"$target_gen_dir/torque-generated/class-definitions-from-dsl.h",
"$target_gen_dir/torque-generated/class-verifiers-from-dsl.cc",
"$target_gen_dir/torque-generated/class-verifiers-from-dsl.h",
"$target_gen_dir/torque-generated/objects-printer-from-dsl.cc",
]
foreach(namespace, torque_namespaces) {
@ -1119,6 +1121,8 @@ v8_source_set("torque_generated_definitions") {
]
sources = [
"$target_gen_dir/torque-generated/class-verifiers-from-dsl.cc",
"$target_gen_dir/torque-generated/class-verifiers-from-dsl.h",
"$target_gen_dir/torque-generated/objects-printer-from-dsl.cc",
]

View File

@ -78,7 +78,10 @@ type Number = Smi | HeapNumber;
type BigInt extends HeapObject generates 'TNode<BigInt>';
type Numeric = Number | BigInt;
extern class Name extends HeapObject { hash_field: int32; }
@noVerifier
extern class Name extends HeapObject {
hash_field: int32;
}
extern class Symbol extends Name {
flags: int32;
@ -93,6 +96,7 @@ extern class ConsString extends String {
second: String;
}
@noVerifier
extern class ExternalString extends String {
resource: RawPtr;
resource_data: RawPtr;
@ -104,7 +108,9 @@ extern class ExternalTwoByteString extends ExternalString {}
extern class InternalizedString extends String {}
// TODO(v8:8983): Add declaration for variable-sized region.
extern class SeqString extends String {}
@noVerifier
extern class SeqString extends String {
}
extern class SeqOneByteString extends SeqString {}
extern class SeqTwoByteString extends SeqString {}
@ -118,7 +124,9 @@ extern class ThinString extends String { actual: String; }
// The HeapNumber value NaN
type NaN extends HeapNumber;
extern class Struct extends HeapObject {}
@noVerifier
extern class Struct extends HeapObject {
}
@generatePrint
extern class Tuple2 extends Struct {
@ -139,7 +147,10 @@ type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
type Map extends HeapObject generates 'TNode<Map>';
extern class FixedArrayBase extends HeapObject { length: Smi; }
@noVerifier
extern class FixedArrayBase extends HeapObject {
length: Smi;
}
extern class FixedArray extends FixedArrayBase { objects[length]: Object; }
@ -180,13 +191,16 @@ intrinsic %GetAllocationBaseSize<Class: type>(map: Map): intptr;
intrinsic %Allocate<Class: type>(size: intptr): Class;
intrinsic %AllocateInternalClass<Class: type>(slotCount: constexpr intptr): Class;
@noVerifier
extern class JSReceiver extends HeapObject {
properties_or_hash: FixedArrayBase | Smi;
}
type Constructor extends JSReceiver;
extern class JSObject extends JSReceiver { elements: FixedArrayBase; }
extern class JSObject extends JSReceiver {
@noVerifier elements: FixedArrayBase;
}
macro NewJSObject(
map: Map, properties: FixedArrayBase | Smi,
@ -207,9 +221,9 @@ macro NewJSObject(implicit context: Context)(): JSObject {
extern class JSFunction extends JSObject {
shared_function_info: SharedFunctionInfo;
context: Context;
feedback_cell: Smi;
feedback_cell: FeedbackCell;
weak code: Code;
weak prototype_or_initial_map: JSReceiver | Map;
@noVerifier weak prototype_or_initial_map: JSReceiver | Map;
}
extern class JSProxy extends JSReceiver {
@ -217,6 +231,7 @@ extern class JSProxy extends JSReceiver {
handler: Object;
}
@noVerifier
extern class JSProxyRevocableResult extends JSObject {
proxy: Object;
revoke: Object;
@ -238,6 +253,7 @@ extern class JSGlobalProxy extends JSObject { native_context: Object; }
extern class JSValue extends JSObject { value: Object; }
extern class JSArgumentsObject extends JSObject {}
@noVerifier
extern class JSArgumentsObjectWithLength extends JSArgumentsObject {
length: Object;
}
@ -306,10 +322,14 @@ type NoSharedNameSentinel extends Smi;
type JSModuleNamespace extends JSObject;
type WeakArrayList extends HeapObject;
extern class JSWeakCollection extends JSObject { table: Object; }
@noVerifier
extern class JSWeakCollection extends JSObject {
table: Object;
}
extern class JSWeakSet extends JSWeakCollection {}
extern class JSWeakMap extends JSWeakCollection {}
@noVerifier
extern class JSCollectionIterator extends JSObject {
table: Object;
index: Object;
@ -336,7 +356,7 @@ extern class PrototypeInfo extends Struct {
prototype_users: WeakArrayList | Zero;
registry_slot: Smi;
validity_cell: Object;
object_create_map: Smi | WeakArrayList;
@noVerifier object_create_map: Smi | WeakArrayList;
bit_field: Smi;
}
@ -377,7 +397,7 @@ extern class SharedFunctionInfo extends HeapObject {
weak function_data: Object;
name_or_scope_info: String | NoSharedNameSentinel | ScopeInfo;
outer_scope_info_or_feedback_metadata: HeapObject;
script_or_debug_info: Script | DebugInfo;
script_or_debug_info: Script | DebugInfo | Undefined;
length: int16;
formal_parameter_count: uint16;
// Currently set to uint16, can be set to uint8 to save space.
@ -412,7 +432,7 @@ type NumberDictionary extends HeapObject
extern class FreeSpace extends HeapObject {
size: Smi;
next: FreeSpace;
@noVerifier next: FreeSpace;
}
// %RawDownCast should *never* be used anywhere in Torque code except for
@ -504,6 +524,7 @@ extern class JSTypedArray extends JSArrayBufferView {
length: Smi;
}
@noVerifier
extern class JSAccessorPropertyDescriptor extends JSObject {
get: Object;
set: Object;
@ -511,7 +532,10 @@ extern class JSAccessorPropertyDescriptor extends JSObject {
configurable: Object;
}
extern class JSCollection extends JSObject { table: Object; }
@noVerifier
extern class JSCollection extends JSObject {
table: Object;
}
extern class JSSet extends JSCollection {}
extern class JSMap extends JSCollection {}
@ -542,6 +566,7 @@ extern class JSStringIterator extends JSObject {
next_index: Smi;
}
@noVerifier
extern class JSDataPropertyDescriptor extends JSObject {
value: Object;
writable: Object;
@ -575,7 +600,7 @@ extern class FunctionTemplateInfo extends TemplateInfo {
function_template_rare_data: Object;
shared_function_info: Object;
flag: Smi;
length: Smi;
@noVerifier length: Smi;
cached_property_name: Object;
}
@ -621,13 +646,13 @@ const UTF16:
extern class Foreign extends HeapObject { foreign_address: RawPtr; }
extern class InterceptorInfo extends Struct {
getter: Foreign | Zero;
setter: Foreign | Zero;
query: Foreign | Zero;
descriptor: Foreign | Zero;
deleter: Foreign | Zero;
enumerator: Foreign | Zero;
definer: Foreign | Zero;
@noVerifier getter: Foreign | Zero;
@noVerifier setter: Foreign | Zero;
@noVerifier query: Foreign | Zero;
@noVerifier descriptor: Foreign | Zero;
@noVerifier deleter: Foreign | Zero;
@noVerifier enumerator: Foreign | Zero;
@noVerifier definer: Foreign | Zero;
data: Object;
flags: Smi;
}
@ -651,9 +676,9 @@ extern class Cell extends HeapObject { value: Object; }
extern class DataHandler extends Struct {
smi_handler: Smi | Code;
validity_cell: Smi | Cell;
weak data_1: Object;
weak data_2: Object;
weak data_3: Object;
@noVerifier weak data_1: Object;
@noVerifier weak data_2: Object;
@noVerifier weak data_3: Object;
}
extern class JSGeneratorObject extends JSObject {
@ -733,9 +758,9 @@ extern class WasmCapiFunctionData extends Struct {
extern class WasmDebugInfo extends Struct {
instance: WasmInstanceObject;
interpreter_handle: Foreign | Undefined;
locals_names: FixedArray;
c_wasm_entries: FixedArray;
c_wasm_entry_map: Foreign; // Managed<wasm::SignatureMap>
locals_names: FixedArray | Undefined;
c_wasm_entries: FixedArray | Undefined;
c_wasm_entry_map: Foreign | Undefined; // Managed<wasm::SignatureMap>
}
extern class WasmExceptionTag extends Struct { index: Smi; }
@ -976,7 +1001,7 @@ extern class PromiseReaction extends Struct {
extern class PromiseReactionJobTask extends Microtask {
argument: Object;
context: Context;
handler: Callable | Undefined;
@noVerifier handler: Callable | Undefined;
promise_or_capability: JSPromise | PromiseCapability | Undefined;
}
@ -997,6 +1022,7 @@ extern class JSRegExp extends JSObject {
flags: Smi | Undefined;
}
@noVerifier
extern class JSIteratorResult extends JSObject {
value: Object;
done: Boolean;
@ -1069,9 +1095,9 @@ extern class AccessorInfo extends Struct {
name: Object;
flags: Smi;
expected_receiver_type: Object;
setter: Foreign | Zero;
getter: Foreign | Zero;
js_getter: Foreign | Zero;
@noVerifier setter: Foreign | Zero;
@noVerifier getter: Foreign | Zero;
@noVerifier js_getter: Foreign | Zero;
data: Object;
}
@ -1085,7 +1111,7 @@ extern class BreakPointInfo extends Tuple2 {}
extern class CoverageInfo extends FixedArray {}
extern class DebugInfo extends Struct {
shared_function_info: HeapObject;
shared_function_info: SharedFunctionInfo;
debugger_hints: Smi;
script: Undefined | Script;
original_bytecode_array: Undefined | BytecodeArray;
@ -1098,7 +1124,7 @@ extern class DebugInfo extends Struct {
extern class FeedbackVector extends HeapObject {
shared_function_info: SharedFunctionInfo;
// TODO(v8:9108): currently no support for MaybeObject in Torque
optimized_code_weak_or_smi: Object;
@noVerifier optimized_code_weak_or_smi: Object;
closure_feedback_cell_array: FixedArray;
length: int32;
invocation_count: int32;
@ -1113,7 +1139,7 @@ extern class FeedbackCell extends Struct {
type AllocationSite extends Struct;
extern class AllocationMemento extends Struct {
allocation_site: AllocationSite;
@noVerifier allocation_site: AllocationSite;
}
extern class WasmModuleObject extends JSObject {
@ -1121,8 +1147,8 @@ extern class WasmModuleObject extends JSObject {
export_wrappers: FixedArray;
script: Script;
weak_instance_list: WeakArrayList;
asm_js_offset_table: ByteArray;
break_point_infos: FixedArray;
asm_js_offset_table: ByteArray | Undefined;
break_point_infos: FixedArray | Undefined;
}
extern class WasmTableObject extends JSObject {
@ -1135,7 +1161,7 @@ extern class WasmTableObject extends JSObject {
extern class WasmMemoryObject extends JSObject {
array_buffer: JSArrayBuffer;
maximum_pages: Smi;
instances: WeakArrayList;
instances: WeakArrayList | Undefined;
}
extern class WasmGlobalObject extends JSObject {
@ -1150,9 +1176,13 @@ extern class WasmExceptionObject extends JSObject {
exception_tag: HeapObject;
}
extern class WasmExceptionPackage extends JSReceiver {}
@noVerifier
extern class WasmExceptionPackage extends JSReceiver {
}
extern class WasmExportedFunction extends JSFunction {}
@noVerifier
extern class WasmExportedFunction extends JSFunction {
}
extern class AsmWasmData extends Struct {
managed_native_module: Foreign; // Managed<wasm::NativeModule>

File diff suppressed because it is too large Load Diff

View File

@ -121,6 +121,12 @@ bool Object::IsNullOrUndefined() const {
return IsHeapObject() && HeapObject::cast(*this)->IsNullOrUndefined();
}
bool Object::IsZero() const { return *this == Smi::zero(); }
bool Object::IsNoSharedNameSentinel() const {
return *this == SharedFunctionInfo::kNoSharedNameSentinel;
}
bool HeapObject::IsNullOrUndefined(Isolate* isolate) const {
return Object::IsNullOrUndefined(isolate);
}

View File

@ -306,6 +306,9 @@ class Object {
V8_INLINE bool IsNullOrUndefined(ReadOnlyRoots roots) const;
V8_INLINE bool IsNullOrUndefined() const;
V8_INLINE bool IsZero() const;
V8_INLINE bool IsNoSharedNameSentinel() const;
enum class Conversion { kToNumber, kToNumeric };
#define DECL_STRUCT_PREDICATE(NAME, Name, name) V8_INLINE bool Is##Name() const;

View File

@ -201,6 +201,8 @@ class FixedArray : public FixedArrayBase {
using BodyDescriptor = FlexibleBodyDescriptor<kHeaderSize>;
static constexpr int kObjectsOffset = kHeaderSize;
protected:
// Set operation on FixedArray without using write barriers. Can
// only be used for storing old space objects or smis.

View File

@ -721,6 +721,7 @@ struct ClassFieldExpression {
base::Optional<std::string> conditional;
bool weak;
bool const_qualified;
bool generate_verify;
};
struct LabelAndTypes {
@ -924,7 +925,7 @@ struct StructDeclaration : TypeDeclaration {
struct ClassDeclaration : TypeDeclaration {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ClassDeclaration)
ClassDeclaration(SourcePosition pos, Identifier* name, bool is_extern,
bool generate_print, bool transient,
bool generate_print, bool generate_verify, bool transient,
base::Optional<TypeExpression*> super,
base::Optional<std::string> generates,
std::vector<Declaration*> methods,
@ -932,6 +933,7 @@ struct ClassDeclaration : TypeDeclaration {
: TypeDeclaration(kKind, pos, name),
is_extern(is_extern),
generate_print(generate_print),
generate_verify(generate_verify),
transient(transient),
super(super),
generates(std::move(generates)),
@ -939,6 +941,7 @@ struct ClassDeclaration : TypeDeclaration {
fields(std::move(fields)) {}
bool is_extern;
bool generate_print;
bool generate_verify;
bool transient;
base::Optional<TypeExpression*> super;
base::Optional<std::string> generates;

View File

@ -2965,6 +2965,179 @@ void ImplementationVisitor::GeneratePrintDefinitions(std::string& file_name) {
ReplaceFileContentsIfDifferent(file_name, new_contents);
}
namespace {
void GenerateClassFieldVerifier(const std::string& class_name,
const ClassType& class_type, const Field& f,
std::ostream& h_contents,
std::ostream& cc_contents) {
if (!f.generate_verify) return;
const Type* field_type = f.name_and_type.type;
// We only verify tagged types, not raw numbers or pointers.
if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) return;
if (f.index) {
if ((*f.index)->name_and_type.type != TypeOracle::GetSmiType()) {
ReportError("Non-SMI values are not (yet) supported as indexes.");
}
// We already verified the index field because it was listed earlier, so we
// can assume it's safe to read here.
cc_contents << " for (int i = 0; i < Smi::ToInt(READ_FIELD(o, "
<< class_name << "::k"
<< CamelifyString((*f.index)->name_and_type.name)
<< "Offset)); ++i) {\n";
} else {
cc_contents << " {\n";
}
const char* object_type = f.is_weak ? "MaybeObject" : "Object";
const char* read_fn = f.is_weak ? "READ_WEAK_FIELD" : "READ_FIELD";
const char* verify_fn =
f.is_weak ? "VerifyMaybeObjectPointer" : "VerifyPointer";
const char* index_offset = f.index ? " + i * kTaggedSize" : "";
// Name the local var based on the field name for nicer CHECK output.
const std::string value = f.name_and_type.name + "_value";
// Read the field.
cc_contents << " " << object_type << " " << value << " = " << read_fn
<< "(o, " << class_name << "::k"
<< CamelifyString(f.name_and_type.name) << "Offset"
<< index_offset << ");\n";
// Call VerifyPointer or VerifyMaybeObjectPointer on it.
cc_contents << " " << object_type << "::" << verify_fn << "(isolate, "
<< value << ");\n";
// Check that the value is of an appropriate type. We can skip this part for
// the Object type because it would not check anything beyond what we already
// checked with VerifyPointer.
if (f.name_and_type.type != TypeOracle::GetObjectType()) {
std::string type_check = f.is_weak ? value + ".IsWeakOrCleared()" : "";
std::string strong_value =
value + (f.is_weak ? ".GetHeapObjectOrSmi()" : "");
for (const std::string& runtime_type : field_type->GetRuntimeTypes()) {
if (!type_check.empty()) type_check += " || ";
type_check += strong_value + ".Is" + runtime_type + "()";
}
// Many subtypes of JSObject can be verified in partially-initialized states
// where their fields are all undefined. We explicitly allow that here. For
// any such fields that should never be undefined, we can include extra code
// in the custom verifier functions for them.
// TODO(1240798): If Factory::InitializeJSObjectFromMap is updated to use
// correct initial values based on the type of the field, then make this
// check stricter too.
if (class_type.IsSubtypeOf(TypeOracle::GetJSObjectType())) {
type_check += " || " + strong_value + ".IsUndefined(isolate)";
}
cc_contents << " CHECK(" << type_check << ");\n";
}
cc_contents << " }\n";
}
} // namespace
void ImplementationVisitor::GenerateClassVerifiers(
const std::string& output_directory) {
const char* file_name = "class-verifiers-from-dsl";
std::stringstream h_contents;
std::stringstream cc_contents;
h_contents << "#ifndef V8_CLASS_VERIFIERS_FROM_DSL_H_\n"
"#define V8_CLASS_VERIFIERS_FROM_DSL_H_\n"
"\n";
const char* enabled_check = "\n#ifdef VERIFY_HEAP\n";
h_contents << enabled_check;
cc_contents << enabled_check;
h_contents << "\n#include \"src/objects.h\"\n";
for (const std::string& include_path : GlobalContext::CppIncludes()) {
cc_contents << "#include " << StringLiteralQuote(include_path) << "\n";
}
cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n";
cc_contents
<< "\n// Has to be the last include (doesn't have include guards):\n"
"#include \"src/objects/object-macros.h\"\n";
const char* namespaces =
"\nnamespace v8 {\n"
"namespace internal {\n"
"\n";
h_contents << namespaces;
cc_contents << namespaces;
const char* verifier_class = "ClassVerifiersFromDSL";
h_contents << "class " << verifier_class << "{\n";
h_contents << " public:\n";
for (auto i : GlobalContext::GetClasses()) {
ClassType* type = i.second;
if (!type->IsExtern() || !type->ShouldGenerateVerify()) continue;
std::string method_name = i.first + "Verify";
h_contents << " static void " << method_name << "(" << i.first
<< " o, Isolate* isolate);\n";
cc_contents << "void " << verifier_class << "::" << method_name << "("
<< i.first << " o, Isolate* isolate) {\n";
// First, do any verification for the super class. Not all classes have
// verifiers, so skip to the nearest super class that has one.
const ClassType* super_type = type->GetSuperClass();
while (super_type && !super_type->ShouldGenerateVerify()) {
super_type = super_type->GetSuperClass();
}
if (super_type) {
std::string super_name = super_type->name();
if (super_name == "HeapObject") {
// Special case: HeapObjectVerify checks the Map type and dispatches to
// more specific types, so calling it here would cause infinite
// recursion. We could consider moving that behavior into a different
// method to make the contract of *Verify methods more consistent, but
// for now we'll just avoid the bad case.
cc_contents << " " << super_name << "Verify(o, isolate);\n";
} else {
cc_contents << " o->" << super_name << "Verify(isolate);\n";
}
}
// Second, verify that this object is what it claims to be.
cc_contents << " CHECK(o.Is" << i.first << "());\n";
// Third, verify its properties.
for (auto f : type->fields()) {
GenerateClassFieldVerifier(i.first, *i.second, f, h_contents,
cc_contents);
}
cc_contents << "}\n";
}
h_contents << "};\n";
const char* end_namespaces =
"\n} // namespace internal\n"
"} // namespace v8\n";
h_contents << end_namespaces;
cc_contents << end_namespaces;
cc_contents << "\n#include \"src/objects/object-macros-undef.h\"\n";
const char* end_enabled_check = "\n#endif // VERIFY_HEAP\n";
h_contents << end_enabled_check;
cc_contents << end_enabled_check;
h_contents << "\n#endif // V8_CLASS_VERIFIERS_FROM_DSL_H_\n";
ReplaceFileContentsIfDifferent(output_directory + "/" + file_name + ".h",
h_contents.str());
ReplaceFileContentsIfDifferent(output_directory + "/" + file_name + ".cc",
cc_contents.str());
}
} // namespace torque
} // namespace internal
} // namespace v8

View File

@ -278,6 +278,7 @@ class ImplementationVisitor {
void GenerateBuiltinDefinitions(std::string& file_name);
void GenerateClassDefinitions(std::string& file_name);
void GeneratePrintDefinitions(std::string& file_name);
void GenerateClassVerifiers(const std::string& output_directory);
VisitResult Visit(Expression* expr);
const Type* Visit(Statement* stmt);

View File

@ -88,6 +88,8 @@ void CompileCurrentAst(TorqueCompilerOptions options) {
output_directory + "/objects-printer-from-dsl.cc";
implementation_visitor.GeneratePrintDefinitions(output_source_path);
implementation_visitor.GenerateClassVerifiers(output_directory);
for (Namespace* n : GlobalContext::Get().GetNamespaces()) {
implementation_visitor.EndNamespaceFile(n);
implementation_visitor.GenerateImplementation(output_directory, n);

View File

@ -656,8 +656,9 @@ class AnnotationSet {
base::Optional<ParseResult> MakeClassDeclaration(
ParseResultIterator* child_results) {
AnnotationSet annotations(child_results, {"@generatePrint"});
AnnotationSet annotations(child_results, {"@generatePrint", "@noVerifier"});
bool generate_print = annotations.Contains("@generatePrint");
bool generate_verify = !annotations.Contains("@noVerifier");
auto is_extern = child_results->NextAs<bool>();
auto transient = child_results->NextAs<bool>();
auto name = child_results->NextAs<Identifier*>();
@ -681,8 +682,8 @@ base::Optional<ParseResult> MakeClassDeclaration(
});
Declaration* result = MakeNode<ClassDeclaration>(
name, is_extern, generate_print, transient, std::move(extends),
std::move(generates), std::move(methods), fields);
name, is_extern, generate_print, generate_verify, transient,
std::move(extends), std::move(generates), std::move(methods), fields);
return ParseResult{result};
}
@ -1258,13 +1259,19 @@ base::Optional<ParseResult> MakeNameAndExpressionFromExpression(
base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
auto conditional = child_results->NextAs<base::Optional<std::string>>();
AnnotationSet annotations(child_results, {"@noVerifier"});
bool generate_verify = !annotations.Contains("@noVerifier");
auto weak = child_results->NextAs<bool>();
auto const_qualified = child_results->NextAs<bool>();
auto name = child_results->NextAs<Identifier*>();
auto index = child_results->NextAs<base::Optional<std::string>>();
auto type = child_results->NextAs<TypeExpression*>();
return ParseResult{ClassFieldExpression{
{name, type}, index, conditional, weak, const_qualified}};
return ParseResult{ClassFieldExpression{{name, type},
index,
conditional,
weak,
const_qualified,
generate_verify}};
}
base::Optional<ParseResult> MakeStructField(
@ -1484,7 +1491,7 @@ struct TorqueGrammar : Grammar {
Symbol classField = {Rule(
{Optional<std::string>(
Sequence({Token("@ifdef"), Token("("), &identifier, Token(")")})),
CheckIf(Token("weak")), CheckIf(Token("const")), &name,
annotations, CheckIf(Token("weak")), CheckIf(Token("const")), &name,
optionalArraySpecifier, Token(":"), &type, Token(";")},
MakeClassField)};

View File

@ -35,12 +35,13 @@ class TypeOracle : public ContextualClass<TypeOracle> {
static ClassType* GetClassType(const Type* parent, const std::string& name,
bool is_extern, bool generate_print,
bool transient, const std::string& generates,
bool generate_verify, bool transient,
const std::string& generates,
ClassDeclaration* decl,
const TypeAlias* alias) {
ClassType* result =
new ClassType(parent, CurrentNamespace(), name, is_extern,
generate_print, transient, generates, decl, alias);
ClassType* result = new ClassType(
parent, CurrentNamespace(), name, is_extern, generate_print,
generate_verify, transient, generates, decl, alias);
Get().struct_types_.push_back(std::unique_ptr<ClassType>(result));
return result;
}

View File

@ -116,7 +116,8 @@ const StructType* TypeVisitor::ComputeType(StructDeclaration* decl) {
{field.name_and_type.name->value, field_type},
offset,
false,
field.const_qualified});
field.const_qualified,
false});
offset += LoweredSlotCount(field_type);
}
DeclareMethods(struct_type, decl->methods);
@ -151,7 +152,7 @@ const ClassType* TypeVisitor::ComputeType(ClassDeclaration* decl) {
new_class = TypeOracle::GetClassType(
super_type, decl->name->value, decl->is_extern, decl->generate_print,
decl->transient, generates, decl, alias);
decl->generate_verify, decl->transient, generates, decl, alias);
} else {
if (decl->super) {
ReportError("Only extern classes can inherit.");
@ -161,7 +162,8 @@ const ClassType* TypeVisitor::ComputeType(ClassDeclaration* decl) {
}
new_class = TypeOracle::GetClassType(
TypeOracle::GetTaggedType(), decl->name->value, decl->is_extern,
decl->generate_print, decl->transient, "FixedArray", decl, alias);
decl->generate_print, decl->generate_verify, decl->transient,
"FixedArray", decl, alias);
}
GlobalContext::RegisterClass(decl->name->value, new_class);
return new_class;
@ -248,7 +250,8 @@ void TypeVisitor::VisitClassFieldsAndMethods(
{field_expression.name_and_type.name->value, field_type},
class_offset,
field_expression.weak,
field_expression.const_qualified});
field_expression.const_qualified,
field_expression.generate_verify});
} else {
if (seen_indexed_field) {
ReportError("cannot declare non-indexable field \"",
@ -263,7 +266,8 @@ void TypeVisitor::VisitClassFieldsAndMethods(
{field_expression.name_and_type.name->value, field_type},
class_offset,
field_expression.weak,
field_expression.const_qualified});
field_expression.const_qualified,
field_expression.generate_verify});
size_t field_size;
std::string size_string;
std::string machine_type;

View File

@ -285,20 +285,19 @@ std::vector<Method*> AggregateType::Methods(const std::string& name) const {
std::string StructType::ToExplicitString() const {
std::stringstream result;
result << "struct " << name() << "{";
PrintCommaSeparatedList(result, fields());
result << "}";
result << "struct " << name();
return result.str();
}
ClassType::ClassType(const Type* parent, Namespace* nspace,
const std::string& name, bool is_extern,
bool generate_print, bool transient,
bool generate_print, bool generate_verify, bool transient,
const std::string& generates, const ClassDeclaration* decl,
const TypeAlias* alias)
: AggregateType(Kind::kClassType, parent, nspace, name),
is_extern_(is_extern),
generate_print_(generate_print),
generate_verify_(generate_verify),
transient_(transient),
size_(0),
has_indexed_field_(false),
@ -326,9 +325,7 @@ std::string ClassType::GetGeneratedTypeNameImpl() const {
std::string ClassType::ToExplicitString() const {
std::stringstream result;
result << "class " << name() << "{";
PrintCommaSeparatedList(result, fields());
result << "}";
result << "class " << name();
return result.str();
}

View File

@ -103,6 +103,7 @@ class Type : public TypeBase {
virtual bool IsTransient() const { return false; }
virtual const Type* NonConstexprVersion() const { return this; }
base::Optional<const ClassType*> ClassSupertype() const;
virtual std::vector<std::string> GetRuntimeTypes() const { return {}; }
static const Type* CommonSupertype(const Type* a, const Type* b);
void AddAlias(std::string alias) const { aliases_.insert(std::move(alias)); }
@ -154,6 +155,7 @@ struct Field {
size_t offset;
bool is_weak;
bool const_qualified;
bool generate_verify;
};
std::ostream& operator<<(std::ostream& os, const Field& name_and_type);
@ -210,6 +212,7 @@ class AbstractType final : public Type {
if (non_constexpr_version_) return non_constexpr_version_;
return this;
}
std::vector<std::string> GetRuntimeTypes() const override { return {name()}; }
private:
friend class TypeOracle;
@ -397,6 +400,15 @@ class UnionType final : public Type {
return union_type ? UnionType(*union_type) : UnionType(t);
}
std::vector<std::string> GetRuntimeTypes() const override {
std::vector<std::string> result;
for (const Type* member : types_) {
std::vector<std::string> sub_result = member->GetRuntimeTypes();
result.insert(result.end(), sub_result.begin(), sub_result.end());
}
return result;
}
private:
explicit UnionType(const Type* t) : Type(Kind::kUnionType, t), types_({t}) {}
void RecomputeParent();
@ -444,6 +456,7 @@ class AggregateType : public Type {
std::vector<Method*> Methods(const std::string& name) const;
std::vector<const AggregateType*> GetHierarchy() const;
std::vector<std::string> GetRuntimeTypes() const override { return {name_}; }
protected:
AggregateType(Kind kind, const Type* parent, Namespace* nspace,
@ -496,6 +509,7 @@ class ClassType final : public AggregateType {
std::string GetGeneratedTNodeTypeNameImpl() const override;
bool IsExtern() const { return is_extern_; }
bool ShouldGeneratePrint() const { return generate_print_; }
bool ShouldGenerateVerify() const { return generate_verify_; }
bool IsTransient() const override { return transient_; }
bool HasIndexedField() const override;
size_t size() const { return size_; }
@ -518,12 +532,13 @@ class ClassType final : public AggregateType {
friend class TypeOracle;
friend class TypeVisitor;
ClassType(const Type* parent, Namespace* nspace, const std::string& name,
bool is_extern, bool generate_print, bool transient,
const std::string& generates, const ClassDeclaration* decl,
const TypeAlias* alias);
bool is_extern, bool generate_print, bool generate_verify,
bool transient, const std::string& generates,
const ClassDeclaration* decl, const TypeAlias* alias);
bool is_extern_;
bool generate_print_;
bool generate_verify_;
bool transient_;
size_t size_;
mutable bool has_indexed_field_;

View File

@ -770,6 +770,7 @@ namespace test {
assert(a.b.GetX() == 2);
}
@noVerifier
extern class TestClassWithAllTypes extends JSObject {
a: int8;
b: uint8;
@ -786,6 +787,7 @@ namespace test {
// This class should throw alignment errors if @ifdef decorators aren't
// working.
@noVerifier
extern class PreprocessingTest extends JSObject {
@ifdef(FALSE_FOR_TESTING) a: int8;
@ifdef(TRUE_FOR_TESTING) a: int16;
@ -894,8 +896,12 @@ namespace test {
type Baztype = Foo | FooType;
extern class Foo extends JSObject { fooField: FooType; }
@noVerifier
extern class Foo extends JSObject {
fooField: FooType;
}
@noVerifier
extern class Bar extends Foo {
barField: Bartype;
bazfield: Baztype;