diff --git a/include/flatbuffers/code_generators.h b/include/flatbuffers/code_generators.h index 6b27ea780..95fa0c1ac 100644 --- a/include/flatbuffers/code_generators.h +++ b/include/flatbuffers/code_generators.h @@ -21,17 +21,70 @@ namespace flatbuffers { class BaseGenerator { public: - BaseGenerator(const Parser &parser, const std::string &path, - const std::string &file_name) - : parser_(parser), path_(path), file_name_(file_name){}; virtual bool generate() = 0; + static const std::string NamespaceDir(const Parser &parser, + const std::string &path) { + EnsureDirExists(path.c_str()); + if (parser.opts.one_file) return path; + std::string namespace_dir = path; // Either empty or ends in separator. + auto &namespaces = parser.namespaces_.back()->components; + for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { + namespace_dir += *it + kPathSeparator; + EnsureDirExists(namespace_dir.c_str()); + } + return namespace_dir; + } + protected: + BaseGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : parser_(parser), + path_(path), + file_name_(file_name), + namespace_dir_(BaseGenerator::NamespaceDir(parser, path)){}; virtual ~BaseGenerator(){}; + // No copy/assign. + BaseGenerator &operator=(const BaseGenerator &); + BaseGenerator(const BaseGenerator &); + + const char *FlatBuffersGeneratedWarning() { + return "automatically generated by the FlatBuffers compiler," + " do not modify\n\n"; + } + + bool IsEverythingGenerated() { + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + if (!(*it)->generated) return false; + } + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + if (!(*it)->generated) return false; + } + return true; + } + + std::string FullNamespace(const char *separator) { + std::string namespace_name; + auto &namespaces = parser_.namespaces_.back()->components; + for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { + if (namespace_name.length()) namespace_name += separator; + namespace_name += *it; + } + return namespace_name; + } + + const std::string LastNamespacePart() { + auto &namespaces = parser_.namespaces_.back()->components; + if (namespaces.size()) return *(namespaces.end() - 1); else return std::string(""); + } + const Parser &parser_; const std::string &path_; const std::string &file_name_; + const std::string namespace_dir_; }; } // namespace flatbuffers diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index b22aaaa6a..ff83da6d2 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -337,7 +337,7 @@ public: // Change elements if you have a non-const pointer to this object. // Scalars only. See reflection.h, and the documentation. - void Mutate(uoffset_t i, T val) { + void Mutate(uoffset_t i, const T& val) { assert(i < size()); WriteScalar(data() + i, val); } @@ -1161,6 +1161,17 @@ template const T *GetRoot(const void *buf) { return GetMutableRoot(const_cast(buf)); } +/// Helpers to get a typed pointer to objects that are currently beeing built. +/// @warning Creating new objects will lead to reallocations and invalidates the pointer! +template T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) { + return reinterpret_cast(fbb.GetCurrentBufferPointer() + + fbb.GetSize() - offset.o); +} + +template const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset offset) { + return GetMutableTemporaryPointer(fbb, offset); +} + // Helper to see if the identifier in a buffer has the expected value. inline bool BufferHasIdentifier(const void *buf, const char *identifier) { return strncmp(reinterpret_cast(buf) + sizeof(uoffset_t), @@ -1495,6 +1506,35 @@ volatile __attribute__((weak)) const char *flatbuffer_version_string = #endif // !defined(_WIN32) && !defined(__CYGWIN__) +#define DEFINE_BITMASK_OPERATORS(E, T)\ + inline E operator | (E lhs, E rhs){\ + return E(T(lhs) | T(rhs));\ + }\ + inline E operator & (E lhs, E rhs){\ + return E(T(lhs) & T(rhs));\ + }\ + inline E operator ^ (E lhs, E rhs){\ + return E(T(lhs) ^ T(rhs));\ + }\ + inline E operator ~ (E lhs){\ + return E(~T(lhs));\ + }\ + inline E operator |= (E &lhs, E rhs){\ + lhs = lhs | rhs;\ + return lhs;\ + }\ + inline E operator &= (E &lhs, E rhs){\ + lhs = lhs & rhs;\ + return lhs;\ + }\ + inline E operator ^= (E &lhs, E rhs){\ + lhs = lhs ^ rhs;\ + return lhs;\ + }\ + inline bool operator !(E rhs) \ + {\ + return !bool(T(rhs)); \ + } /// @endcond } // namespace flatbuffers diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index 794ce65f0..7efa1334e 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -166,6 +166,7 @@ static void GenEnum(const Parser &parser, EnumDef &enum_def, if (parser.opts.scoped_enums) code += " : " + GenTypeBasic(enum_def.underlying_type, false); code += " {\n"; + int64_t anyv = 0; EnumVal *minv = nullptr, *maxv = nullptr; for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); @@ -176,13 +177,26 @@ static void GenEnum(const Parser &parser, EnumDef &enum_def, code += NumToString(ev.value) + ",\n"; minv = !minv || minv->value > ev.value ? &ev : minv; maxv = !maxv || maxv->value < ev.value ? &ev : maxv; + anyv |= ev.value; } - assert(minv && maxv); - code += " " + GenEnumVal(enum_def, "MIN", parser.opts) + " = "; - code += GenEnumVal(enum_def, minv->name, parser.opts) + ",\n"; - code += " " + GenEnumVal(enum_def, "MAX", parser.opts) + " = "; - code += GenEnumVal(enum_def, maxv->name, parser.opts) + "\n"; - code += "};\n\n"; + if (parser.opts.scoped_enums || parser.opts.prefixed_enums) { + assert(minv && maxv); + if (enum_def.attributes.Lookup("bit_flags")) { + if (minv->value != 0) // If the user didn't defined NONE value + code += " " + GenEnumVal(enum_def, "NONE", parser.opts) + " = 0,\n"; + if (maxv->value != anyv) // If the user didn't defined ANY value + code += " " + GenEnumVal(enum_def, "ANY", parser.opts) + " = " + NumToString(anyv) + "\n"; + } else { // MIN & MAX are useless for bit_flags + code += " " + GenEnumVal(enum_def, "MIN", parser.opts) + " = "; + code += GenEnumVal(enum_def, minv->name, parser.opts) + ",\n"; + code += " " + GenEnumVal(enum_def, "MAX", parser.opts) + " = "; + code += GenEnumVal(enum_def, maxv->name, parser.opts) + "\n"; + } + } + code += "};\n"; + if (parser.opts.scoped_enums && enum_def.attributes.Lookup("bit_flags")) + code += "DEFINE_BITMASK_OPERATORS(" + enum_def.name + ", " + GenTypeBasic(enum_def.underlying_type, false) + ")\n"; + code += "\n"; // Generate a generate string table for enum values. // Problem is, if values are very sparse that could generate really big @@ -730,26 +744,10 @@ class CppGenerator : public BaseGenerator { // structs, // and tables) and output them to a single file. bool generate() { - // Check if we have any code to generate at all, to avoid an empty header. - for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); - ++it) { - if (!(*it)->generated) goto generate_code; - } - for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); - ++it) { - if (!(*it)->generated) goto generate_code; - } - // No code to generate, exit: - return true; - - generate_code: - - using namespace cpp; + if (IsEverythingGenerated()) return true; std::string code; - code = - "// automatically generated by the FlatBuffers compiler," - " do not modify\n\n"; + code = code + "// " + FlatBuffersGeneratedWarning(); // Generate include guard. std::string include_guard_ident = file_name_; diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 4a4e54dc4..a0b837a20 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -1115,40 +1115,6 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += "};\n\n"; } -// Save out the generated code for a single class while adding -// declaration boilerplate. -static bool SaveClass(const LanguageParameters &lang, const Parser &parser, - const std::string &defname, const std::string &classcode, - const std::string &path, bool needs_includes, bool onefile) { - if (!classcode.length()) return true; - - std::string namespace_general; - std::string namespace_dir = path; // Either empty or ends in separator. - auto &namespaces = parser.namespaces_.back()->components; - for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { - if (namespace_general.length()) { - namespace_general += "."; - } - namespace_general += *it; - if (!onefile) { - namespace_dir += *it + kPathSeparator; - } - - } - EnsureDirExists(namespace_dir); - - std::string code = "// automatically generated, do not modify\n\n"; - if (!namespace_general.empty()) { - code += lang.namespace_ident + namespace_general + lang.namespace_begin; - code += "\n\n"; - } - if (needs_includes) code += lang.includes; - code += classcode; - if (!namespace_general.empty()) code += lang.namespace_end; - auto filename = namespace_dir + defname + lang.file_extension; - return SaveFile(filename.c_str(), code, false); -} - namespace general { class GeneralGenerator : public BaseGenerator { public: @@ -1167,9 +1133,7 @@ class GeneralGenerator : public BaseGenerator { if (parser_.opts.one_file) { one_file_code += enumcode; } else { - if (!SaveClass(lang, parser_, (**it).name, enumcode, path_, false, - false)) - return false; + if (!SaveType(lang, (**it).name, enumcode, false)) return false; } } @@ -1180,18 +1144,35 @@ class GeneralGenerator : public BaseGenerator { if (parser_.opts.one_file) { one_file_code += declcode; } else { - if (!SaveClass(lang, parser_, (**it).name, declcode, path_, true, - false)) - return false; + if (!SaveType(lang, (**it).name, declcode, true)) return false; } } if (parser_.opts.one_file) { - return SaveClass(lang, parser_, file_name_, one_file_code, path_, true, - true); + return SaveType(lang, file_name_, one_file_code, true); } return true; } + + // Save out the generated code for a single class while adding + // declaration boilerplate. + bool SaveType(const LanguageParameters &lang, const std::string &defname, + const std::string &classcode, bool needs_includes) { + if (!classcode.length()) return true; + + std::string code; + code = code + "// " + FlatBuffersGeneratedWarning(); + std::string namespace_name = FullNamespace("."); + if (!namespace_name.empty()) { + code += lang.namespace_ident + namespace_name + lang.namespace_begin; + code += "\n\n"; + } + if (needs_includes) code += lang.includes; + code += classcode; + if (!namespace_name.empty()) code += lang.namespace_end; + auto filename = namespace_dir_ + defname + lang.file_extension; + return SaveFile(filename.c_str(), code, false); + } }; } // namespace general @@ -1201,50 +1182,30 @@ bool GenerateGeneral(const Parser &parser, const std::string &path, return generator.generate(); } -static std::string ClassFileName(const LanguageParameters &lang, - const Parser &parser, const Definition &def, - const std::string &path) { - std::string namespace_general; - std::string namespace_dir = path; - auto &namespaces = parser.namespaces_.back()->components; - for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { - if (namespace_general.length()) { - namespace_general += "."; - namespace_dir += kPathSeparator; - } - namespace_general += *it; - namespace_dir += *it; - } - - return namespace_dir + kPathSeparator + def.name + lang.file_extension; -} - -std::string GeneralMakeRule(const Parser &parser, - const std::string &path, +std::string GeneralMakeRule(const Parser &parser, const std::string &path, const std::string &file_name) { assert(parser.opts.lang <= IDLOptions::kMAX); auto lang = language_parameters[parser.opts.lang]; std::string make_rule; + std::string directory = + BaseGenerator::NamespaceDir(parser, path) + kPathSeparator; - for (auto it = parser.enums_.vec.begin(); - it != parser.enums_.vec.end(); ++it) { - if (make_rule != "") - make_rule += " "; - make_rule += ClassFileName(lang, parser, **it, path); + for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end(); + ++it) { + if (make_rule != "") make_rule += " "; + make_rule += directory + (**it).name + lang.file_extension; } - for (auto it = parser.structs_.vec.begin(); - it != parser.structs_.vec.end(); ++it) { - if (make_rule != "") - make_rule += " "; - make_rule += ClassFileName(lang, parser, **it, path); + for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end(); + ++it) { + if (make_rule != "") make_rule += " "; + make_rule += directory + (**it).name + lang.file_extension; } make_rule += ": "; auto included_files = parser.GetIncludedFilesRecursive(file_name); - for (auto it = included_files.begin(); - it != included_files.end(); ++it) { + for (auto it = included_files.begin(); it != included_files.end(); ++it) { make_rule += " " + *it; } return make_rule; diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index bce8f95bc..5841e121c 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -53,20 +53,6 @@ std::string OffsetPrefix(const FieldDef &field) { "))\n\tif o != 0 {\n"; } -// Begin by declaring namespace and imports. -static void BeginFile(const std::string name_space_name, - const bool needs_imports, - std::string *code_ptr) { - std::string &code = *code_ptr; - code += "// automatically generated, do not modify\n\n"; - code += "package " + name_space_name + "\n\n"; - if (needs_imports) { - code += "import (\n"; - code += "\tflatbuffers \"github.com/google/flatbuffers/go\"\n"; - code += ")\n"; - } -} - // Begin a class declaration. static void BeginClass(const StructDef &struct_def, std::string *code_ptr) { std::string &code = *code_ptr; @@ -589,32 +575,6 @@ static std::string GenMethod(const FieldDef &field) { : (IsStruct(field.value.type) ? "Struct" : "UOffsetT"); } - -// Save out the generated code for a Go Table type. -static bool SaveType(const Parser &parser, const Definition &def, - const std::string &classcode, const std::string &path, - bool needs_imports) { - if (!classcode.length()) return true; - - std::string namespace_name; - std::string namespace_dir = path; // Either empty or ends in separator. - auto &namespaces = parser.namespaces_.back()->components; - for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { - if (namespace_name.length()) { - namespace_name += "."; - } - namespace_name = *it; - namespace_dir += *it + kPathSeparator; - } - EnsureDirExists(namespace_dir); - - std::string code = ""; - BeginFile(namespace_name, needs_imports, &code); - code += classcode; - std::string filename = namespace_dir + def.name + ".go"; - return SaveFile(filename.c_str(), code, false); -} - static std::string GenTypeBasic(const Type &type) { static const char *ctypename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ @@ -671,18 +631,44 @@ class GoGenerator : public BaseGenerator { ++it) { std::string enumcode; go::GenEnum(**it, &enumcode); - if (!go::SaveType(parser_, **it, enumcode, path_, false)) return false; + if (!SaveType(**it, enumcode, false)) return false; } for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); ++it) { std::string declcode; go::GenStruct(**it, &declcode, parser_.root_struct_def_); - if (!go::SaveType(parser_, **it, declcode, path_, true)) return false; + if (!SaveType(**it, declcode, true)) return false; } return true; } + + private: + // Begin by declaring namespace and imports. + void BeginFile(const std::string name_space_name, const bool needs_imports, + std::string *code_ptr) { + std::string &code = *code_ptr; + code = code + "// " + FlatBuffersGeneratedWarning(); + code += "package " + name_space_name + "\n\n"; + if (needs_imports) { + code += "import (\n"; + code += "\tflatbuffers \"github.com/google/flatbuffers/go\"\n"; + code += ")\n"; + } + } + + // Save out the generated code for a Go Table type. + bool SaveType(const Definition &def, const std::string &classcode, + bool needs_imports) { + if (!classcode.length()) return true; + + std::string code = ""; + BeginFile(LastNamespacePart(), needs_imports, &code); + code += classcode; + std::string filename = namespace_dir_ + def.name + ".go"; + return SaveFile(filename.c_str(), code, false); + } }; } // namespace go diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp index de81f2764..d724b80f4 100644 --- a/src/idl_gen_js.cpp +++ b/src/idl_gen_js.cpp @@ -678,31 +678,27 @@ class JsGenerator : public BaseGenerator { // Iterate through all definitions we haven't generate code for (enums, // structs, and tables) and output them to a single file. bool generate() { + if (IsEverythingGenerated()) return true; + std::string enum_code, struct_code, exports_code, code; generateEnums(&enum_code, &exports_code); generateStructs(&struct_code, &exports_code); - // Only output file-level code if there were any declarations. - if (enum_code.length() || struct_code.length()) { - code += - "// automatically generated by the FlatBuffers compiler, do not " - "modify\n\n"; + code = code + "// " + FlatBuffersGeneratedWarning(); - // Generate code for all the namespace declarations. - GenNamespaces(parser_, &code, &exports_code); + // Generate code for all the namespace declarations. + GenNamespaces(parser_, &code, &exports_code); - // Output the main declaration code from above. - code += enum_code; - code += struct_code; + // Output the main declaration code from above. + code += enum_code; + code += struct_code; - if (!exports_code.empty() && !parser_.opts.skip_js_exports) { - code += "// Exports for Node.js and RequireJS\n"; - code += exports_code; - } + if (!exports_code.empty() && !parser_.opts.skip_js_exports) { + code += "// Exports for Node.js and RequireJS\n"; + code += exports_code; } - return !code.length() || - SaveFile(GeneratedFileName(path_, file_name_).c_str(), code, false); + return SaveFile(GeneratedFileName(path_, file_name_).c_str(), code, false); } private: diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp index b43ea6702..599d5571d 100644 --- a/src/idl_gen_php.cpp +++ b/src/idl_gen_php.cpp @@ -54,24 +54,6 @@ namespace php { // Hardcode spaces per indentation. const std::string Indent = " "; - // Begin by declaring namespace and imports. - static void BeginFile(const std::string name_space_name, - const bool needs_imports, - std::string *code_ptr) { - std::string &code = *code_ptr; - code += "components; - for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { - if (namespace_name.length()) { - namespace_name += "\\"; - namespace_dir += kPathSeparator; - } - namespace_name += *it; - namespace_dir += *it; - EnsureDirExists(namespace_dir.c_str()); - } - - std::string code = ""; - BeginFile(namespace_name, needs_imports, &code); - code += classcode; - - std::string filename = namespace_dir + kPathSeparator + def.name + ".php"; - return SaveFile(filename.c_str(), code, false); - } - static std::string GenTypeBasic(const Type &type) { static const char *ctypename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ @@ -993,8 +946,7 @@ namespace php { auto &enum_def = **it; std::string enumcode; GenEnum(enum_def, &enumcode); - if (!SaveType(parser_, enum_def, enumcode, path_, false)) - return false; + if (!SaveType(enum_def, enumcode, false)) return false; } return true; } @@ -1005,11 +957,41 @@ namespace php { auto &struct_def = **it; std::string declcode; GenStruct(parser_, struct_def, &declcode); - if (!SaveType(parser_, struct_def, declcode, path_, true)) - return false; + if (!SaveType(struct_def, declcode, true)) return false; } return true; } + + // Begin by declaring namespace and imports. + void BeginFile(const std::string name_space_name, + const bool needs_imports, std::string *code_ptr) { + std::string &code = *code_ptr; + code += "components; - for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { - if (namespace_name.length()) { - namespace_name += "."; - namespace_dir += kPathSeparator; - } - namespace_name = *it; - namespace_dir += *it; - EnsureDirExists(namespace_dir.c_str()); - - std::string init_py_filename = namespace_dir + "/__init__.py"; - SaveFile(init_py_filename.c_str(), "", false); - } - - - std::string code = ""; - BeginFile(namespace_name, needs_imports, &code); - code += classcode; - std::string filename = namespace_dir + kPathSeparator + def.name + ".py"; - return SaveFile(filename.c_str(), code, false); -} - static std::string GenTypeBasic(const Type &type) { static const char *ctypename[] = { #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ @@ -653,7 +610,7 @@ class PythonGenerator : public BaseGenerator { auto &enum_def = **it; std::string enumcode; GenEnum(enum_def, &enumcode); - if (!SaveType(parser_, enum_def, enumcode, path_, false)) return false; + if (!SaveType(enum_def, enumcode, false)) return false; } return true; } @@ -664,10 +621,42 @@ class PythonGenerator : public BaseGenerator { auto &struct_def = **it; std::string declcode; GenStruct(struct_def, &declcode, parser_.root_struct_def_); - if (!SaveType(parser_, struct_def, declcode, path_, true)) return false; + if (!SaveType(struct_def, declcode, true)) return false; } return true; } + + // Begin by declaring namespace and imports. + void BeginFile(const std::string name_space_name, const bool needs_imports, + std::string *code_ptr) { + std::string &code = *code_ptr; + code = code + "# " + FlatBuffersGeneratedWarning(); + code += "# namespace: " + name_space_name + "\n\n"; + if (needs_imports) { + code += "import flatbuffers\n\n"; + } + } + + // Save out the generated code for a Python Table type. + bool SaveType(const Definition &def, const std::string &classcode, + bool needs_imports) { + if (!classcode.length()) return true; + + std::string namespace_dir = path_; + auto &namespaces = parser_.namespaces_.back()->components; + for (auto it = namespaces.begin(); it != namespaces.end(); ++it) { + if (it != namespaces.begin()) namespace_dir += kPathSeparator; + namespace_dir += *it; + std::string init_py_filename = namespace_dir + "/__init__.py"; + SaveFile(init_py_filename.c_str(), "", false); + } + + std::string code = ""; + BeginFile(LastNamespacePart(), needs_imports, &code); + code += classcode; + std::string filename = namespace_dir_ + kPathSeparator + def.name + ".py"; + return SaveFile(filename.c_str(), code, false); + } }; } // namespace python diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index a606781b5..f5badab16 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -236,12 +236,19 @@ CheckedError Parser::Next() { if(!isdigit(static_cast(*cursor_))) return NoError(); return Error("floating point constant can\'t start with \".\""); case '\"': - case '\'': + case '\'': { + int unicode_high_surrogate = -1; + while (*cursor_ != c) { if (*cursor_ < ' ' && *cursor_ >= 0) return Error("illegal character in string constant"); if (*cursor_ == '\\') { cursor_++; + if (unicode_high_surrogate != -1 && + *cursor_ != 'u') { + return Error( + "illegal Unicode sequence (unpaired high surrogate)"); + } switch (*cursor_) { case 'n': attribute_ += '\n'; cursor_++; break; case 't': attribute_ += '\t'; cursor_++; break; @@ -263,18 +270,51 @@ CheckedError Parser::Next() { cursor_++; int64_t val; ECHECK(ParseHexNum(4, &val)); - ToUTF8(static_cast(val), &attribute_); + if (val >= 0xD800 && val <= 0xDBFF) { + if (unicode_high_surrogate != -1) { + return Error( + "illegal Unicode sequence (multiple high surrogates)"); + } else { + unicode_high_surrogate = static_cast(val); + } + } else if (val >= 0xDC00 && val <= 0xDFFF) { + if (unicode_high_surrogate == -1) { + return Error( + "illegal Unicode sequence (unpaired low surrogate)"); + } else { + int code_point = 0x10000 + + ((unicode_high_surrogate & 0x03FF) << 10) + + (val & 0x03FF); + ToUTF8(code_point, &attribute_); + unicode_high_surrogate = -1; + } + } else { + if (unicode_high_surrogate != -1) { + return Error( + "illegal Unicode sequence (unpaired high surrogate)"); + } + ToUTF8(static_cast(val), &attribute_); + } break; } default: return Error("unknown escape code in string constant"); } } else { // printable chars + UTF-8 bytes + if (unicode_high_surrogate != -1) { + return Error( + "illegal Unicode sequence (unpaired high surrogate)"); + } attribute_ += *cursor_++; } } + if (unicode_high_surrogate != -1) { + return Error( + "illegal Unicode sequence (unpaired high surrogate)"); + } cursor_++; token_ = kTokenStringConstant; return NoError(); + } case '/': if (*cursor_ == '/') { const char *start = ++cursor_; @@ -1961,14 +2001,14 @@ flatbuffers::Offset> attrs; - for (auto kv : attributes.dict) { - auto it = parser.known_attributes_.find(kv.first); + for (auto kv = attributes.dict.begin(); kv != attributes.dict.end(); ++kv) { + auto it = parser.known_attributes_.find(kv->first); assert(it != parser.known_attributes_.end()); if (!it->second) { // Custom attribute. attrs.push_back( - reflection::CreateKeyValue(*builder, builder->CreateString(kv.first), + reflection::CreateKeyValue(*builder, builder->CreateString(kv->first), builder->CreateString( - kv.second->constant))); + kv->second->constant))); } } if (attrs.size()) { diff --git a/tests/MyGame/Example/Any.cs b/tests/MyGame/Example/Any.cs index a05a91851..1f018ad89 100644 --- a/tests/MyGame/Example/Any.cs +++ b/tests/MyGame/Example/Any.cs @@ -1,4 +1,4 @@ -// automatically generated, do not modify +// automatically generated by the FlatBuffers compiler, do not modify namespace MyGame.Example { diff --git a/tests/MyGame/Example/Any.go b/tests/MyGame/Example/Any.go index a23de4cbc..0322364c6 100644 --- a/tests/MyGame/Example/Any.go +++ b/tests/MyGame/Example/Any.go @@ -1,4 +1,4 @@ -// automatically generated, do not modify +// automatically generated by the FlatBuffers compiler, do not modify package Example diff --git a/tests/MyGame/Example/Any.java b/tests/MyGame/Example/Any.java index cdc3ec567..27dc1bcb9 100644 --- a/tests/MyGame/Example/Any.java +++ b/tests/MyGame/Example/Any.java @@ -1,4 +1,4 @@ -// automatically generated, do not modify +// automatically generated by the FlatBuffers compiler, do not modify package MyGame.Example; diff --git a/tests/MyGame/Example/Any.php b/tests/MyGame/Example/Any.php index d35bfd639..f04f4ad3f 100644 --- a/tests/MyGame/Example/Any.php +++ b/tests/MyGame/Example/Any.php @@ -1,5 +1,5 @@ ( + parser.builder_.GetBufferPointer()); + auto string = root->GetPointer( + flatbuffers::FieldIndexToOffset(0)); + TEST_EQ(strcmp(string->c_str(), "\xF0\x9F\x92\xA9"), 0); +} + +void UnicodeInvalidSurrogatesTest() { + TestError( + "table T { F:string; }" + "root_type T;" + "{ F:\"\\uD800\"}", "unpaired high surrogate"); + TestError( + "table T { F:string; }" + "root_type T;" + "{ F:\"\\uD800abcd\"}", "unpaired high surrogate"); + TestError( + "table T { F:string; }" + "root_type T;" + "{ F:\"\\uD800\\n\"}", "unpaired high surrogate"); + TestError( + "table T { F:string; }" + "root_type T;" + "{ F:\"\\uD800\\uD800\"}", "multiple high surrogates"); + TestError( + "table T { F:string; }" + "root_type T;" + "{ F:\"\\uDC00\"}", "unpaired low surrogate"); +} + void UnknownFieldsTest() { flatbuffers::IDLOptions opts; opts.skip_unexpected_fields_in_json = true; @@ -916,6 +954,8 @@ int main(int /*argc*/, const char * /*argv*/[]) { EnumStringsTest(); IntegerOutOfRangeTest(); UnicodeTest(); + UnicodeSurrogatesTest(); + UnicodeInvalidSurrogatesTest(); UnknownFieldsTest(); if (!testing_fails) {