mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-07 22:03:40 +00:00
[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:
@@ -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")) {
|
||||
|
||||
Reference in New Issue
Block a user