Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion include/flatbuffers/idl.h
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,10 @@ struct IDLOptions {
/******************************* Python gRPC ********************************/
bool grpc_python_typed_handlers;

// If set, when a float value is received for an integer field, set the value
// to 0 to avoid precision loss.
bool zero_on_float_to_int;

IDLOptions()
: gen_jvmstatic(false),
use_flexbuffers(false),
Expand Down Expand Up @@ -861,7 +865,8 @@ struct IDLOptions {
set_empty_vectors_to_null(true),
grpc_filename_suffix(".fb"),
grpc_use_system_headers(true),
grpc_python_typed_handlers(false) {}
grpc_python_typed_handlers(false),
zero_on_float_to_int(false) {}
};

// This encapsulates where the parser is in the current source file.
Expand Down
74 changes: 45 additions & 29 deletions src/idl_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,11 +948,13 @@ CheckedError Parser::ParseField(StructDef &struct_def) {
// For union fields, add a second auto-generated field to hold the type,
// with a special suffix.

// To ensure compatibility with many codes that rely on the BASE_TYPE_UTYPE value to identify union type fields.
// To ensure compatibility with many codes that rely on the BASE_TYPE_UTYPE
// value to identify union type fields.
Type union_type(type.enum_def->underlying_type);
union_type.base_type = BASE_TYPE_UTYPE;
ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(),union_type, &typefield));

ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(), union_type,
&typefield));

} else if (IsVector(type) && type.element == BASE_TYPE_UNION) {
advanced_features_ |= reflection::AdvancedUnionFeatures;
// Only cpp, js and ts supports the union vector feature so far.
Expand Down Expand Up @@ -1590,7 +1592,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
if (!struct_def.sortbysize ||
size == SizeOf(field_value.type.base_type)) {
switch (field_value.type.base_type) {
// clang-format off
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \
case BASE_TYPE_ ## ENUM: \
builder_.Pad(field->padding); \
Expand Down Expand Up @@ -1733,7 +1735,7 @@ CheckedError Parser::ParseVector(const Type &vector_type, uoffset_t *ovalue,
// start at the back, since we're building the data backwards.
auto &val = field_stack_.back().first;
switch (val.type.base_type) {
// clang-format off
// clang-format off
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE,...) \
case BASE_TYPE_ ## ENUM: \
if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
Expand Down Expand Up @@ -2189,6 +2191,21 @@ CheckedError Parser::ParseSingleValue(const std::string *name, Value &e,
} else {
// Try a float number.
TRY_ECHECK(kTokenFloatConstant, IsFloat(in_type), BASE_TYPE_FLOAT);

// Special handling for integer types that might come as floats from JSON
// This happens when large integers are parsed as doubles by JSON parsers
// Set the value to 0 to avoid precision issues
if (!match && token_ == kTokenFloatConstant && IsInteger(in_type) &&
opts.zero_on_float_to_int) {
double float_val = std::stod(attribute_);
// For integers fields that receive float values, set to 0
Warning("Float value " + std::to_string(float_val) + " received for <" +
std::string(TypeName(in_type)) +
"> field, setting to 0.");
attribute_ = "0";
TRY_ECHECK(kTokenFloatConstant, IsInteger(in_type), BASE_TYPE_INT);
}

// Integer token can init any scalar (integer of float).
FORCE_ECHECK(kTokenIntegerConstant, IsScalar(in_type), BASE_TYPE_INT);
}
Expand Down Expand Up @@ -2372,12 +2389,8 @@ template<typename T> void EnumDef::ChangeEnumValue(EnumVal *ev, T new_value) {
}

namespace EnumHelper {
template<BaseType E> struct EnumValType {
typedef int64_t type;
};
template<> struct EnumValType<BASE_TYPE_ULONG> {
typedef uint64_t type;
};
template<BaseType E> struct EnumValType { typedef int64_t type; };
template<> struct EnumValType<BASE_TYPE_ULONG> { typedef uint64_t type; };
} // namespace EnumHelper

struct EnumValBuilder {
Expand Down Expand Up @@ -2482,8 +2495,8 @@ CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest,
EnumDef *enum_def;
ECHECK(StartEnum(enum_name, is_union, &enum_def));
if (filename != nullptr && !opts.project_root.empty()) {
enum_def->declaration_file =
&GetPooledString(FilePath(opts.project_root, filename, opts.binary_schema_absolute_paths));
enum_def->declaration_file = &GetPooledString(FilePath(
opts.project_root, filename, opts.binary_schema_absolute_paths));
}
enum_def->doc_comment = enum_comment;
if (!opts.proto_mode) {
Expand Down Expand Up @@ -2511,14 +2524,15 @@ CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest,
if (explicit_underlying_type) {
// Specify the integer type underlying this enum.
ECHECK(ParseType(enum_def->underlying_type));
if (!IsInteger(enum_def->underlying_type.base_type) || IsBool(enum_def->underlying_type.base_type)) {
return Error("underlying " + std::string(is_union ? "union" : "enum") + "type must be integral");
if (!IsInteger(enum_def->underlying_type.base_type) ||
IsBool(enum_def->underlying_type.base_type)) {
return Error("underlying " + std::string(is_union ? "union" : "enum") +
"type must be integral");
}

// Make this type refer back to the enum it was derived from.
enum_def->underlying_type.enum_def = enum_def;
}

}
ECHECK(ParseMetaData(&enum_def->attributes));
const auto underlying_type = enum_def->underlying_type.base_type;
Expand Down Expand Up @@ -2682,8 +2696,7 @@ bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions &opts) {
IDLOptions::kKotlin | IDLOptions::kKotlinKmp | IDLOptions::kCpp |
IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kTs |
IDLOptions::kBinary | IDLOptions::kGo | IDLOptions::kPython |
IDLOptions::kJson |
IDLOptions::kNim;
IDLOptions::kJson | IDLOptions::kNim;
unsigned long langs = opts.lang_to_generate;
return (langs > 0 && langs < IDLOptions::kMAX) && !(langs & ~supported_langs);
}
Expand Down Expand Up @@ -2719,8 +2732,8 @@ bool Parser::Supports64BitOffsets() const {
}

bool Parser::SupportsUnionUnderlyingType() const {
return (opts.lang_to_generate & ~(IDLOptions::kCpp | IDLOptions::kTs |
IDLOptions::kBinary)) == 0;
return (opts.lang_to_generate &
~(IDLOptions::kCpp | IDLOptions::kTs | IDLOptions::kBinary)) == 0;
}

Namespace *Parser::UniqueNamespace(Namespace *ns) {
Expand Down Expand Up @@ -2761,8 +2774,8 @@ CheckedError Parser::ParseDecl(const char *filename) {
struct_def->doc_comment = dc;
struct_def->fixed = fixed;
if (filename && !opts.project_root.empty()) {
struct_def->declaration_file =
&GetPooledString(FilePath(opts.project_root, filename, opts.binary_schema_absolute_paths));
struct_def->declaration_file = &GetPooledString(FilePath(
opts.project_root, filename, opts.binary_schema_absolute_paths));
}
ECHECK(ParseMetaData(&struct_def->attributes));
struct_def->sortbysize =
Expand Down Expand Up @@ -2855,8 +2868,8 @@ CheckedError Parser::ParseService(const char *filename) {
service_def.doc_comment = service_comment;
service_def.defined_namespace = current_namespace_;
if (filename != nullptr && !opts.project_root.empty()) {
service_def.declaration_file =
&GetPooledString(FilePath(opts.project_root, filename, opts.binary_schema_absolute_paths));
service_def.declaration_file = &GetPooledString(FilePath(
opts.project_root, filename, opts.binary_schema_absolute_paths));
}
if (services_.Add(current_namespace_->GetFullyQualifiedName(service_name),
&service_def))
Expand Down Expand Up @@ -3937,12 +3950,12 @@ void Parser::Serialize() {
std::vector<Offset<flatbuffers::String>> included_files;
for (auto f = files_included_per_file_.begin();
f != files_included_per_file_.end(); f++) {

const auto filename__ = builder_.CreateSharedString(FilePath(
opts.project_root, f->first, opts.binary_schema_absolute_paths));
for (auto i = f->second.begin(); i != f->second.end(); i++) {
included_files.push_back(builder_.CreateSharedString(
FilePath(opts.project_root, i->filename, opts.binary_schema_absolute_paths)));
FilePath(opts.project_root, i->filename,
opts.binary_schema_absolute_paths)));
}
const auto included_files__ = builder_.CreateVector(included_files);
included_files.clear();
Expand Down Expand Up @@ -4456,8 +4469,11 @@ std::string Parser::ConformTo(const Parser &base) {
}
}
// Check underlying type changes
if (enum_def_base->underlying_type.base_type != enum_def.underlying_type.base_type) {
return "underlying type differ for " + std::string(enum_def.is_union ? "union: " : "enum: ") + qualified_name;
if (enum_def_base->underlying_type.base_type !=
enum_def.underlying_type.base_type) {
return "underlying type differ for " +
std::string(enum_def.is_union ? "union: " : "enum: ") +
qualified_name;
}
}
return "";
Expand Down
15 changes: 15 additions & 0 deletions tests/parser_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -928,5 +928,20 @@ void FieldIdentifierTest() {
#endif
}

void ZeroOnFloatToIntTest() {
flatbuffers::IDLOptions opts;
{
opts.zero_on_float_to_int = true;
flatbuffers::Parser parser(opts);
TEST_EQ(true, parser.Parse("table T{ i: int = 1.0; }"));
}

{
opts.zero_on_float_to_int = false;
flatbuffers::Parser parser(opts);
TEST_EQ(false, parser.Parse("table T{ i: int = 1.0; }"));
}
}

} // namespace tests
} // namespace flatbuffers
1 change: 1 addition & 0 deletions tests/parser_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ void ValidSameNameDifferentNamespaceTest();
void WarningsAsErrorsTest();
void StringVectorDefaultsTest();
void FieldIdentifierTest();
void ZeroOnFloatToIntTest();

} // namespace tests
} // namespace flatbuffers
Expand Down
1 change: 1 addition & 0 deletions tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,7 @@ int FlatBufferTests(const std::string &tests_data_path) {
EmbeddedSchemaAccess();
Offset64Tests();
UnionUnderlyingTypeTest();
ZeroOnFloatToIntTest();
return 0;
}
} // namespace
Expand Down