[CS] Verifier (#7850)

* Fix C/C++ Create<Type>Direct with sorted vectors

If a struct has a key the vector has to be sorted. To sort the vector
you can't use "const".

* Changes due to code review

* Improve code readability

* Add generate of JSON schema to string to lib

* option indent_step is supported

* Remove unused variables

* Fix break in test

* Fix style to be consistent with rest of the code

* [TS] Fix reserved words as arguments (#6955)

* [TS] Fix generation of reserved words in object api (#7106)

* [TS] Fix generation of object api

* [TS] Fix MakeCamel -> ConvertCase

* [C#] Fix collision of field name and type name

* [TS] Add test for struct of struct of struct

* Update generated files

* Add missing files

* [TS] Fix query of null/undefined fields in object api

* Add .Net verfier

* Add some fuzz tests for .Net

* Remove additional files

* Fix .net test

* Changes due to PR

* Fix generated files

---------

Co-authored-by: Derek Bailey <derekbailey@google.com>
This commit is contained in:
tira-misu
2023-04-06 00:29:14 +02:00
committed by GitHub
parent 2803983c70
commit 876a64aae1
37 changed files with 1952 additions and 9 deletions

View File

@@ -169,6 +169,7 @@ class CSharpGenerator : public BaseGenerator {
if (!parser_.opts.one_file)
cur_name_space_ = struct_def.defined_namespace;
GenStruct(struct_def, &declcode, parser_.opts);
GenStructVerifier(struct_def, &declcode);
if (parser_.opts.one_file) {
one_file_code += declcode;
} else {
@@ -623,6 +624,173 @@ class CSharpGenerator : public BaseGenerator {
")";
}
// Get the value of a table verification function start
void GetStartOfTableVerifier(const StructDef &struct_def, std::string *code_ptr) {
std::string &code = *code_ptr;
code += "\n";
code += "static public class " + struct_def.name + "Verify\n";
code += "{\n";
code += " static public bool Verify";
code += "(Google.FlatBuffers.Verifier verifier, uint tablePos)\n";
code += " {\n";
code += " return verifier.VerifyTableStart(tablePos)\n";
}
// Get the value of a table verification function end
void GetEndOfTableVerifier(std::string *code_ptr) {
std::string &code = *code_ptr;
code += " && verifier.VerifyTableEnd(tablePos);\n";
code += " }\n";
code += "}\n";
}
std::string GetNestedFlatBufferName(const FieldDef &field) {
std::string name;
if (field.nested_flatbuffer) {
name = NamespacedName(*field.nested_flatbuffer);
} else {
name = "";
}
return name ;
}
// Generate the code to call the appropriate Verify function(s) for a field.
void GenVerifyCall(CodeWriter &code_, const FieldDef &field, const char *prefix) {
code_.SetValue("PRE", prefix);
code_.SetValue("NAME", ConvertCase(field.name, Case::kUpperCamel));
code_.SetValue("REQUIRED", field.IsRequired() ? "Required" : "");
code_.SetValue("REQUIRED_FLAG", field.IsRequired() ? "true" : "false");
code_.SetValue("TYPE", GenTypeGet(field.value.type));
code_.SetValue("INLINESIZE", NumToString(InlineSize(field.value.type)));
code_.SetValue("OFFSET", NumToString(field.value.offset));
if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type)) {
code_.SetValue("ALIGN", NumToString(InlineAlignment(field.value.type)));
code_ +=
"{{PRE}} && verifier.VerifyField(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{INLINESIZE}} /*{{TYPE}}*/, {{ALIGN}}, {{REQUIRED_FLAG}})";
} else {
// TODO - probably code below should go to this 'else' - code_ += "{{PRE}}VerifyOffset{{REQUIRED}}(verifier, {{OFFSET}})\\";
}
switch (field.value.type.base_type) {
case BASE_TYPE_UNION: {
auto union_name = NamespacedName(*field.value.type.enum_def);
code_.SetValue("ENUM_NAME1", field.value.type.enum_def->name);
code_.SetValue("ENUM_NAME", union_name);
code_.SetValue("SUFFIX", UnionTypeFieldSuffix());
// Caution: This construction assumes, that UNION type id element has been created just before union data and
// its offset precedes union. Such assumption is common in flatbuffer implementation
code_.SetValue("TYPE_ID_OFFSET", NumToString(field.value.offset - sizeof(voffset_t)));
code_ += "{{PRE}} && verifier.VerifyUnion(tablePos, {{TYPE_ID_OFFSET}}, "
"{{OFFSET}} /*{{NAME}}*/, {{ENUM_NAME}}Verify.Verify, {{REQUIRED_FLAG}})";
break;
}
case BASE_TYPE_STRUCT: {
if (!field.value.type.struct_def->fixed) {
code_ += "{{PRE}} && verifier.VerifyTable(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{TYPE}}Verify.Verify, {{REQUIRED_FLAG}})";
}
break;
}
case BASE_TYPE_STRING: {
code_ += "{{PRE}} && verifier.VerifyString(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{REQUIRED_FLAG}})";
break;
}
case BASE_TYPE_VECTOR: {
switch (field.value.type.element) {
case BASE_TYPE_STRING: {
code_ += "{{PRE}} && verifier.VerifyVectorOfStrings(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{REQUIRED_FLAG}})";
break;
}
case BASE_TYPE_STRUCT: {
if (!field.value.type.struct_def->fixed) {
code_ += "{{PRE}} && verifier.VerifyVectorOfTables(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{TYPE}}Verify.Verify, {{REQUIRED_FLAG}})";
} else {
code_.SetValue(
"VECTOR_ELEM_INLINESIZE",
NumToString(InlineSize(field.value.type.VectorType())));
code_ +=
"{{PRE}} && "
"verifier.VerifyVectorOfData(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{VECTOR_ELEM_INLINESIZE}} "
"/*{{TYPE}}*/, {{REQUIRED_FLAG}})";
}
break;
}
case BASE_TYPE_UNION: {
// Vectors of unions are not yet supported for go
break;
}
default:
// Generate verifier for vector of data.
// It may be either nested flatbuffer of just vector of bytes
auto nfn = GetNestedFlatBufferName(field);
if (!nfn.empty()) {
code_.SetValue("CPP_NAME", nfn);
// FIXME: file_identifier.
code_ += "{{PRE}} && verifier.VerifyNestedBuffer(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{CPP_NAME}}Verify.Verify, {{REQUIRED_FLAG}})";
} else if (field.flexbuffer) {
code_ += "{{PRE}} && verifier.VerifyNestedBuffer(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, null, {{REQUIRED_FLAG}})";
} else {
code_.SetValue("VECTOR_ELEM_INLINESIZE", NumToString(InlineSize(field.value.type.VectorType())));
code_ +=
"{{PRE}} && verifier.VerifyVectorOfData(tablePos, "
"{{OFFSET}} /*{{NAME}}*/, {{VECTOR_ELEM_INLINESIZE}} /*{{TYPE}}*/, {{REQUIRED_FLAG}})";
}
break;
}
break;
}
default: {
break;
}
}
}
// Generate table constructors, conditioned on its members' types.
void GenTableVerifier(const StructDef &struct_def, std::string *code_ptr) {
CodeWriter code_;
GetStartOfTableVerifier(struct_def, code_ptr);
// Generate struct fields accessors
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto &field = **it;
if (field.deprecated) continue;
GenVerifyCall(code_, field, "");
}
*code_ptr += code_.ToString();
GetEndOfTableVerifier(code_ptr);
}
// Generate struct or table methods.
void GenStructVerifier(const StructDef &struct_def, std::string *code_ptr) {
if (struct_def.generated) return;
// cur_name_space_ = struct_def.defined_namespace;
// Generate verifiers
if (struct_def.fixed) {
// Fixed size structures do not require table members
// verification - instead structure size is verified using VerifyField
} else {
// Create table verification function
GenTableVerifier(struct_def, code_ptr);
}
}
void GenStruct(StructDef &struct_def, std::string *code_ptr,
const IDLOptions &opts) const {
if (struct_def.generated) return;
@@ -688,8 +856,20 @@ class CSharpGenerator : public BaseGenerator {
code += parser_.file_identifier_;
code += "\"); }\n";
}
// Generate the Verify method that checks if a ByteBuffer is save to
// access
code += " public static ";
code += "bool Verify" + struct_def.name + "(ByteBuffer _bb) {";
code += "Google.FlatBuffers.Verifier verifier = new ";
code += "Google.FlatBuffers.Verifier(_bb); ";
code += "return verifier.VerifyBuffer(\"";
code += parser_.file_identifier_;
code += "\", false, " + struct_def.name + "Verify.Verify);";
code += " }\n";
}
}
// Generate the __init method that sets the field in a pre-existing
// accessor object. This is to allow object reuse.
code += " public void __init(int _i, ByteBuffer _bb) ";
@@ -1418,6 +1598,67 @@ class CSharpGenerator : public BaseGenerator {
code += " }\n";
}
std::string GenUnionVerify(const Type &union_type) const {
if (union_type.enum_def) {
const auto &enum_def = *union_type.enum_def;
auto ret =
"\n\nstatic public class " + enum_def.name + "Verify\n";
ret += "{\n";
ret +=
" static public bool Verify(Google.FlatBuffers.Verifier verifier, "
"byte typeId, uint tablePos)\n";
ret += " {\n";
ret += " bool result = true;\n";
const auto union_enum_loop = [&]() {
ret += " switch((" + enum_def.name + ")typeId)\n";
ret += " {\n";
for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
const auto &ev = **it;
if (ev.IsZero()) { continue; }
ret += " case " + Name(enum_def) + "." + Name(ev) + ":\n";
if (IsString(ev.union_type)) {
ret +=
" result = verifier.VerifyUnionString(tablePos);\n";
ret += " break;";
} else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
if (! ev.union_type.struct_def->fixed) {
auto type = GenTypeGet(ev.union_type);
ret += " result = " + type + "Verify.Verify(verifier, tablePos);\n";
} else {
ret += " result = verifier.VerifyUnionData(tablePos, " +
NumToString(InlineSize(ev.union_type)) + ", " +
NumToString(InlineAlignment(ev.union_type)) +
");\n";;
}
ret += " break;";
} else {
FLATBUFFERS_ASSERT(false);
}
ret += "\n";
}
ret += " default: result = true;\n";
ret += " break;\n";
ret += " }\n";
ret += " return result;\n";
};
union_enum_loop();
ret += " }\n";
ret += "}\n";
ret += "\n";
return ret;
}
FLATBUFFERS_ASSERT(0);
return "";
}
void GenEnum_ObjectAPI(EnumDef &enum_def, std::string *code_ptr,
const IDLOptions &opts) const {
auto &code = *code_ptr;
@@ -1493,6 +1734,9 @@ class CSharpGenerator : public BaseGenerator {
code += " }\n";
code += " }\n";
code += "}\n\n";
code += GenUnionVerify(enum_def.underlying_type);
// JsonConverter
if (opts.cs_gen_json_serializer) {
if (enum_def.attributes.Lookup("private")) {