[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:
parent
41bc1cfd80
commit
e483fb2731
4
BUILD.gn
4
BUILD.gn
@ -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",
|
||||
]
|
||||
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user