/src/flatbuffers/src/idl_parser.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2014 Google Inc. All rights reserved. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <algorithm> |
18 | | #include <cmath> |
19 | | #include <iostream> |
20 | | #include <list> |
21 | | #include <string> |
22 | | #include <utility> |
23 | | |
24 | | #include "flatbuffers/base.h" |
25 | | #include "flatbuffers/buffer.h" |
26 | | #include "flatbuffers/idl.h" |
27 | | #include "flatbuffers/reflection_generated.h" |
28 | | #include "flatbuffers/util.h" |
29 | | |
30 | | namespace flatbuffers { |
31 | | |
32 | | // Reflects the version at the compiling time of binary(lib/dll/so). |
33 | 0 | const char* FLATBUFFERS_VERSION() { |
34 | | // clang-format off |
35 | 0 | return |
36 | 0 | FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "." |
37 | 0 | FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MINOR) "." |
38 | 0 | FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION); |
39 | | // clang-format on |
40 | 0 | } |
41 | | |
42 | | namespace { |
43 | | |
44 | | static const double kPi = 3.14159265358979323846; |
45 | | |
46 | | // The enums in the reflection schema should match the ones we use internally. |
47 | | // Compare the last element to check if these go out of sync. |
48 | | static_assert(BASE_TYPE_VECTOR64 == |
49 | | static_cast<BaseType>(reflection::MaxBaseType - 1), |
50 | | "enums don't match"); |
51 | | |
52 | | // Any parsing calls have to be wrapped in this macro, which automates |
53 | | // handling of recursive error checking a bit. It will check the received |
54 | | // CheckedError object, and return straight away on error. |
55 | | #define ECHECK(call) \ |
56 | 34.0M | { \ |
57 | 34.0M | auto ce = (call); \ |
58 | 34.0M | if (ce.Check()) return ce; \ |
59 | 34.0M | } |
60 | | |
61 | | // These two functions are called hundreds of times below, so define a short |
62 | | // form: |
63 | 10.4M | #define NEXT() ECHECK(Next()) |
64 | 7.62M | #define EXPECT(tok) ECHECK(Expect(tok)) |
65 | | |
66 | 3.73k | static bool ValidateUTF8(const std::string& str) { |
67 | 3.73k | const char* s = &str[0]; |
68 | 3.73k | const char* const sEnd = s + str.length(); |
69 | 16.0k | while (s < sEnd) { |
70 | 12.3k | if (FromUTF8(&s) < 0) { |
71 | 83 | return false; |
72 | 83 | } |
73 | 12.3k | } |
74 | 3.65k | return true; |
75 | 3.73k | } |
76 | | |
77 | 0 | static bool IsLowerSnakeCase(const std::string& str) { |
78 | 0 | for (size_t i = 0; i < str.length(); i++) { |
79 | 0 | char c = str[i]; |
80 | 0 | if (!check_ascii_range(c, 'a', 'z') && !is_digit(c) && c != '_') { |
81 | 0 | return false; |
82 | 0 | } |
83 | 0 | } |
84 | 0 | return true; |
85 | 0 | } |
86 | | |
87 | | static void DeserializeDoc(std::vector<std::string>& doc, |
88 | 292 | const Vector<Offset<String>>* documentation) { |
89 | 292 | if (documentation == nullptr) return; |
90 | 24 | for (uoffset_t index = 0; index < documentation->size(); index++) |
91 | 14 | doc.push_back(documentation->Get(index)->str()); |
92 | 10 | } |
93 | | |
94 | 30.3M | static CheckedError NoError() { return CheckedError(false); } |
95 | | |
96 | | template <typename T> |
97 | 846 | static std::string TypeToIntervalString() { |
98 | 846 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + |
99 | 846 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; |
100 | 846 | } idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<unsigned char>() Line | Count | Source | 97 | 161 | static std::string TypeToIntervalString() { | 98 | 161 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 161 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 161 | } |
idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<signed char>() Line | Count | Source | 97 | 152 | static std::string TypeToIntervalString() { | 98 | 152 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 152 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 152 | } |
idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<short>() Line | Count | Source | 97 | 122 | static std::string TypeToIntervalString() { | 98 | 122 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 122 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 122 | } |
idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<unsigned short>() Line | Count | Source | 97 | 149 | static std::string TypeToIntervalString() { | 98 | 149 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 149 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 149 | } |
idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<int>() Line | Count | Source | 97 | 84 | static std::string TypeToIntervalString() { | 98 | 84 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 84 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 84 | } |
idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<unsigned int>() Line | Count | Source | 97 | 119 | static std::string TypeToIntervalString() { | 98 | 119 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 119 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 119 | } |
idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<long>() Line | Count | Source | 97 | 33 | static std::string TypeToIntervalString() { | 98 | 33 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 33 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 33 | } |
idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<unsigned long>() Line | Count | Source | 97 | 26 | static std::string TypeToIntervalString() { | 98 | 26 | return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " + | 99 | 26 | NumToString((flatbuffers::numeric_limits<T>::max)()) + "]"; | 100 | 26 | } |
Unexecuted instantiation: idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<float>() Unexecuted instantiation: idl_parser.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > flatbuffers::(anonymous namespace)::TypeToIntervalString<double>() |
101 | | |
102 | | // atot: template version of atoi/atof: convert a string to an instance of T. |
103 | | template <typename T> |
104 | 450k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { |
105 | 450k | return StringToNumber(s, val); |
106 | 450k | } idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<unsigned short>(char const*, unsigned short*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 58.1k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 58.1k | return StringToNumber(s, val); | 106 | 58.1k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<unsigned int>(char const*, unsigned int*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 48.2k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 48.2k | return StringToNumber(s, val); | 106 | 48.2k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<unsigned char>(char const*, unsigned char*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 118k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 118k | return StringToNumber(s, val); | 106 | 118k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<signed char>(char const*, signed char*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 10.6k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 10.6k | return StringToNumber(s, val); | 106 | 10.6k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<short>(char const*, short*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 11.8k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 11.8k | return StringToNumber(s, val); | 106 | 11.8k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<int>(char const*, int*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 4.43k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 4.43k | return StringToNumber(s, val); | 106 | 4.43k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<long>(char const*, long*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 116k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 116k | return StringToNumber(s, val); | 106 | 116k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<unsigned long>(char const*, unsigned long*, std::__1::integral_constant<bool, false>) Line | Count | Source | 104 | 81.7k | static bool atot_scalar(const char* s, T* val, bool_constant<false>) { | 105 | 81.7k | return StringToNumber(s, val); | 106 | 81.7k | } |
|
107 | | |
108 | | template <typename T> |
109 | 76.3k | static bool atot_scalar(const char* s, T* val, bool_constant<true>) { |
110 | | // Normalize NaN parsed from fbs or json to unsigned NaN. |
111 | 76.3k | if (false == StringToNumber(s, val)) return false; |
112 | 76.2k | *val = (*val != *val) ? std::fabs(*val) : *val; |
113 | 76.2k | return true; |
114 | 76.3k | } idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<float>(char const*, float*, std::__1::integral_constant<bool, true>) Line | Count | Source | 109 | 16.7k | static bool atot_scalar(const char* s, T* val, bool_constant<true>) { | 110 | | // Normalize NaN parsed from fbs or json to unsigned NaN. | 111 | 16.7k | if (false == StringToNumber(s, val)) return false; | 112 | 16.7k | *val = (*val != *val) ? std::fabs(*val) : *val; | 113 | 16.7k | return true; | 114 | 16.7k | } |
idl_parser.cpp:bool flatbuffers::(anonymous namespace)::atot_scalar<double>(char const*, double*, std::__1::integral_constant<bool, true>) Line | Count | Source | 109 | 59.5k | static bool atot_scalar(const char* s, T* val, bool_constant<true>) { | 110 | | // Normalize NaN parsed from fbs or json to unsigned NaN. | 111 | 59.5k | if (false == StringToNumber(s, val)) return false; | 112 | 59.5k | *val = (*val != *val) ? std::fabs(*val) : *val; | 113 | 59.5k | return true; | 114 | 59.5k | } |
|
115 | | |
116 | | template <typename T> |
117 | 526k | static CheckedError atot(const char* s, Parser& parser, T* val) { |
118 | 526k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); |
119 | 526k | if (done) return NoError(); |
120 | 1.29k | if (0 == *val) |
121 | 453 | return parser.Error("invalid number: \"" + std::string(s) + "\""); |
122 | 846 | else |
123 | 846 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + |
124 | 846 | ", constant does not fit " + TypeToIntervalString<T>()); |
125 | 1.29k | } idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<unsigned short>(char const*, flatbuffers::Parser&, unsigned short*) Line | Count | Source | 117 | 58.1k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 58.1k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 58.1k | if (done) return NoError(); | 120 | 209 | if (0 == *val) | 121 | 60 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 149 | else | 123 | 149 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 149 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 209 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<unsigned int>(char const*, flatbuffers::Parser&, unsigned int*) Line | Count | Source | 117 | 48.2k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 48.2k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 48.2k | if (done) return NoError(); | 120 | 150 | if (0 == *val) | 121 | 31 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 119 | else | 123 | 119 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 119 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 150 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<unsigned char>(char const*, flatbuffers::Parser&, unsigned char*) Line | Count | Source | 117 | 118k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 118k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 118k | if (done) return NoError(); | 120 | 236 | if (0 == *val) | 121 | 75 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 161 | else | 123 | 161 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 161 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 236 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<signed char>(char const*, flatbuffers::Parser&, signed char*) Line | Count | Source | 117 | 10.6k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 10.6k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 10.6k | if (done) return NoError(); | 120 | 182 | if (0 == *val) | 121 | 30 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 152 | else | 123 | 152 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 152 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 182 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<short>(char const*, flatbuffers::Parser&, short*) Line | Count | Source | 117 | 11.8k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 11.8k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 11.8k | if (done) return NoError(); | 120 | 183 | if (0 == *val) | 121 | 61 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 122 | else | 123 | 122 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 122 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 183 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<int>(char const*, flatbuffers::Parser&, int*) Line | Count | Source | 117 | 4.43k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 4.43k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 4.43k | if (done) return NoError(); | 120 | 120 | if (0 == *val) | 121 | 36 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 84 | else | 123 | 84 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 84 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 120 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<long>(char const*, flatbuffers::Parser&, long*) Line | Count | Source | 117 | 116k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 116k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 116k | if (done) return NoError(); | 120 | 82 | if (0 == *val) | 121 | 49 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 33 | else | 123 | 33 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 33 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 82 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<unsigned long>(char const*, flatbuffers::Parser&, unsigned long*) Line | Count | Source | 117 | 81.7k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 81.7k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 81.7k | if (done) return NoError(); | 120 | 55 | if (0 == *val) | 121 | 29 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 26 | else | 123 | 26 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 26 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 55 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<float>(char const*, flatbuffers::Parser&, float*) Line | Count | Source | 117 | 16.7k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 16.7k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 16.7k | if (done) return NoError(); | 120 | 44 | if (0 == *val) | 121 | 44 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 0 | else | 123 | 0 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 0 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 44 | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::(anonymous namespace)::atot<double>(char const*, flatbuffers::Parser&, double*) Line | Count | Source | 117 | 59.5k | static CheckedError atot(const char* s, Parser& parser, T* val) { | 118 | 59.5k | auto done = atot_scalar(s, val, bool_constant<is_floating_point<T>::value>()); | 119 | 59.5k | if (done) return NoError(); | 120 | 38 | if (0 == *val) | 121 | 38 | return parser.Error("invalid number: \"" + std::string(s) + "\""); | 122 | 0 | else | 123 | 0 | return parser.Error("invalid number: \"" + std::string(s) + "\"" + | 124 | 0 | ", constant does not fit " + TypeToIntervalString<T>()); | 125 | 38 | } |
|
126 | | template <> |
127 | | CheckedError atot<Offset<void>>(const char* s, Parser& parser, |
128 | 372k | Offset<void>* val) { |
129 | 372k | (void)parser; |
130 | 372k | *val = Offset<void>(atoi(s)); |
131 | 372k | return NoError(); |
132 | 372k | } |
133 | | |
134 | | template <> |
135 | | CheckedError atot<Offset64<void>>(const char* s, Parser& parser, |
136 | 0 | Offset64<void>* val) { |
137 | 0 | (void)parser; |
138 | 0 | *val = Offset64<void>(atoi(s)); |
139 | 0 | return NoError(); |
140 | 0 | } |
141 | | |
142 | | template <typename T> |
143 | | static T* LookupTableByName(const SymbolTable<T>& table, |
144 | | const std::string& name, |
145 | | const Namespace& current_namespace, |
146 | 4.76k | size_t skip_top) { |
147 | 4.76k | const auto& components = current_namespace.components; |
148 | 4.76k | if (table.dict.empty()) return nullptr; |
149 | 4.76k | if (components.size() < skip_top) return nullptr; |
150 | 4.76k | const auto N = components.size() - skip_top; |
151 | 4.76k | std::string full_name; |
152 | 14.2k | for (size_t i = 0; i < N; i++) { |
153 | 9.52k | full_name += components[i]; |
154 | 9.52k | full_name += '.'; |
155 | 9.52k | } |
156 | 5.23k | for (size_t i = N; i > 0; i--) { |
157 | 4.99k | full_name += name; |
158 | 4.99k | auto obj = table.Lookup(full_name); |
159 | 4.99k | if (obj) return obj; |
160 | 474 | auto len = full_name.size() - components[i - 1].size() - 1 - name.size(); |
161 | 474 | full_name.resize(len); |
162 | 474 | } |
163 | 239 | FLATBUFFERS_ASSERT(full_name.empty()); |
164 | 239 | return table.Lookup(name); // lookup in global namespace |
165 | 239 | } idl_parser.cpp:flatbuffers::EnumDef* flatbuffers::(anonymous namespace)::LookupTableByName<flatbuffers::EnumDef>(flatbuffers::SymbolTable<flatbuffers::EnumDef> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, flatbuffers::Namespace const&, unsigned long) Line | Count | Source | 146 | 4.76k | size_t skip_top) { | 147 | 4.76k | const auto& components = current_namespace.components; | 148 | 4.76k | if (table.dict.empty()) return nullptr; | 149 | 4.76k | if (components.size() < skip_top) return nullptr; | 150 | 4.76k | const auto N = components.size() - skip_top; | 151 | 4.76k | std::string full_name; | 152 | 14.2k | for (size_t i = 0; i < N; i++) { | 153 | 9.52k | full_name += components[i]; | 154 | 9.52k | full_name += '.'; | 155 | 9.52k | } | 156 | 5.23k | for (size_t i = N; i > 0; i--) { | 157 | 4.99k | full_name += name; | 158 | 4.99k | auto obj = table.Lookup(full_name); | 159 | 4.99k | if (obj) return obj; | 160 | 474 | auto len = full_name.size() - components[i - 1].size() - 1 - name.size(); | 161 | 474 | full_name.resize(len); | 162 | 474 | } | 163 | 239 | FLATBUFFERS_ASSERT(full_name.empty()); | 164 | 239 | return table.Lookup(name); // lookup in global namespace | 165 | 239 | } |
Unexecuted instantiation: idl_parser.cpp:flatbuffers::StructDef* flatbuffers::(anonymous namespace)::LookupTableByName<flatbuffers::StructDef>(flatbuffers::SymbolTable<flatbuffers::StructDef> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, flatbuffers::Namespace const&, unsigned long) |
166 | | |
167 | | // Declare tokens we'll use. Single character tokens are represented by their |
168 | | // ascii character code (e.g. '{'), others above 256. |
169 | | // clang-format off |
170 | | #define FLATBUFFERS_GEN_TOKENS(TD) \ |
171 | 9.71k | TD(Eof, 256, "end of file") \ |
172 | 9.71k | TD(StringConstant, 257, "string constant") \ |
173 | 9.71k | TD(IntegerConstant, 258, "integer constant") \ |
174 | 9.71k | TD(FloatConstant, 259, "float constant") \ |
175 | 9.71k | TD(Identifier, 260, "identifier") |
176 | | #ifdef __GNUC__ |
177 | | __extension__ // Stop GCC complaining about trailing comma with -Wpendantic. |
178 | | #endif |
179 | | enum { |
180 | | #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME = VALUE, |
181 | | FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN) |
182 | | #undef FLATBUFFERS_TOKEN |
183 | | }; |
184 | | |
185 | 9.71k | static std::string TokenToString(int t) { |
186 | 9.71k | static const char * const tokens[] = { |
187 | 48.5k | #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING, |
188 | 48.5k | FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN) |
189 | 9.71k | #undef FLATBUFFERS_TOKEN |
190 | 9.71k | #define FLATBUFFERS_TD(ENUM, IDLTYPE, ...) \ |
191 | 184k | IDLTYPE, |
192 | 184k | FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) |
193 | 9.71k | #undef FLATBUFFERS_TD |
194 | 9.71k | }; |
195 | 9.71k | if (t < 256) { // A single ascii char token. |
196 | 4.68k | std::string s; |
197 | 4.68k | s.append(1, static_cast<char>(t)); |
198 | 4.68k | return s; |
199 | 5.03k | } else { // Other tokens. |
200 | 5.03k | return tokens[t - 256]; |
201 | 5.03k | } |
202 | 9.71k | } |
203 | | // clang-format on |
204 | | |
205 | 8.19M | static bool IsIdentifierStart(char c) { return is_alpha(c) || (c == '_'); } |
206 | | |
207 | | static bool CompareSerializedScalars(const uint8_t* a, const uint8_t* b, |
208 | 164M | const FieldDef& key) { |
209 | 164M | switch (key.value.type.base_type) { |
210 | 0 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ |
211 | 164M | case BASE_TYPE_##ENUM: { \ |
212 | 164M | CTYPE def = static_cast<CTYPE>(0); \ |
213 | 164M | if (!a || !b) { \ |
214 | 148M | StringToNumber(key.value.constant.c_str(), &def); \ |
215 | 148M | } \ |
216 | 164M | const auto av = a ? ReadScalar<CTYPE>(a) : def; \ |
217 | 164M | const auto bv = b ? ReadScalar<CTYPE>(b) : def; \ |
218 | 164M | return av < bv; \ |
219 | 164M | } |
220 | 164M | FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD) |
221 | 0 | #undef FLATBUFFERS_TD |
222 | 0 | default: { |
223 | 0 | FLATBUFFERS_ASSERT(false && "scalar type expected"); |
224 | 0 | return false; |
225 | 0 | } |
226 | 164M | } |
227 | 164M | } |
228 | | |
229 | | static bool CompareTablesByScalarKey(const Offset<Table>* _a, |
230 | | const Offset<Table>* _b, |
231 | 153M | const FieldDef& key) { |
232 | 153M | const voffset_t offset = key.value.offset; |
233 | | // Indirect offset pointer to table pointer. |
234 | 153M | auto a = reinterpret_cast<const uint8_t*>(_a) + ReadScalar<uoffset_t>(_a); |
235 | 153M | auto b = reinterpret_cast<const uint8_t*>(_b) + ReadScalar<uoffset_t>(_b); |
236 | | // Fetch field address from table. |
237 | 153M | a = reinterpret_cast<const Table*>(a)->GetAddressOf(offset); |
238 | 153M | b = reinterpret_cast<const Table*>(b)->GetAddressOf(offset); |
239 | 153M | return CompareSerializedScalars(a, b, key); |
240 | 153M | } |
241 | | |
242 | | static bool CompareTablesByStringKey(const Offset<Table>* _a, |
243 | | const Offset<Table>* _b, |
244 | 2.06M | const FieldDef& key) { |
245 | 2.06M | const voffset_t offset = key.value.offset; |
246 | | // Indirect offset pointer to table pointer. |
247 | 2.06M | auto a = reinterpret_cast<const uint8_t*>(_a) + ReadScalar<uoffset_t>(_a); |
248 | 2.06M | auto b = reinterpret_cast<const uint8_t*>(_b) + ReadScalar<uoffset_t>(_b); |
249 | | // Fetch field address from table. |
250 | 2.06M | a = reinterpret_cast<const Table*>(a)->GetAddressOf(offset); |
251 | 2.06M | b = reinterpret_cast<const Table*>(b)->GetAddressOf(offset); |
252 | 2.06M | if (a && b) { |
253 | | // Indirect offset pointer to string pointer. |
254 | 2.06M | a += ReadScalar<uoffset_t>(a); |
255 | 2.06M | b += ReadScalar<uoffset_t>(b); |
256 | 2.06M | return *reinterpret_cast<const String*>(a) < |
257 | 2.06M | *reinterpret_cast<const String*>(b); |
258 | 2.06M | } else { |
259 | 0 | return a ? true : false; |
260 | 0 | } |
261 | 2.06M | } |
262 | | |
263 | 252k | static void SwapSerializedTables(Offset<Table>* a, Offset<Table>* b) { |
264 | | // These are serialized offsets, so are relative where they are |
265 | | // stored in memory, so compute the distance between these pointers: |
266 | 252k | ptrdiff_t diff = (b - a) * sizeof(Offset<Table>); |
267 | 252k | FLATBUFFERS_ASSERT(diff >= 0); // Guaranteed by SimpleQsort. |
268 | 252k | auto udiff = static_cast<uoffset_t>(diff); |
269 | 252k | a->o = EndianScalar(ReadScalar<uoffset_t>(a) - udiff); |
270 | 252k | b->o = EndianScalar(ReadScalar<uoffset_t>(b) + udiff); |
271 | 252k | std::swap(*a, *b); |
272 | 252k | } |
273 | | |
274 | | // See below for why we need our own sort :( |
275 | | template <typename T, typename F, typename S> |
276 | | static void SimpleQsort(T* begin, T* end, size_t width, F comparator, |
277 | 401k | S swapper) { |
278 | 401k | if (end - begin <= static_cast<ptrdiff_t>(width)) return; |
279 | 198k | auto l = begin + width; |
280 | 198k | auto r = end; |
281 | 166M | while (l < r) { |
282 | 166M | if (comparator(begin, l)) { |
283 | 78.5k | r -= width; |
284 | 78.5k | swapper(l, r); |
285 | 165M | } else { |
286 | 165M | l += width; |
287 | 165M | } |
288 | 166M | } |
289 | 198k | l -= width; |
290 | 198k | swapper(begin, l); |
291 | 198k | SimpleQsort(begin, l, width, comparator, swapper); |
292 | 198k | SimpleQsort(r, end, width, comparator, swapper); |
293 | 198k | } idl_parser.cpp:void flatbuffers::(anonymous namespace)::SimpleQsort<unsigned char, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_1, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_2>(unsigned char*, unsigned char*, unsigned long, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_1, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_2) Line | Count | Source | 277 | 40.0k | S swapper) { | 278 | 40.0k | if (end - begin <= static_cast<ptrdiff_t>(width)) return; | 279 | 19.8k | auto l = begin + width; | 280 | 19.8k | auto r = end; | 281 | 10.5M | while (l < r) { | 282 | 10.5M | if (comparator(begin, l)) { | 283 | 4.68k | r -= width; | 284 | 4.68k | swapper(l, r); | 285 | 10.5M | } else { | 286 | 10.5M | l += width; | 287 | 10.5M | } | 288 | 10.5M | } | 289 | 19.8k | l -= width; | 290 | 19.8k | swapper(begin, l); | 291 | 19.8k | SimpleQsort(begin, l, width, comparator, swapper); | 292 | 19.8k | SimpleQsort(r, end, width, comparator, swapper); | 293 | 19.8k | } |
idl_parser.cpp:void flatbuffers::(anonymous namespace)::SimpleQsort<flatbuffers::Offset<flatbuffers::Table>, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_3, void (*)(flatbuffers::Offset<flatbuffers::Table>*, flatbuffers::Offset<flatbuffers::Table>*)>(flatbuffers::Offset<flatbuffers::Table>*, flatbuffers::Offset<flatbuffers::Table>*, unsigned long, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_3, void (*)(flatbuffers::Offset<flatbuffers::Table>*, flatbuffers::Offset<flatbuffers::Table>*)) Line | Count | Source | 277 | 84.7k | S swapper) { | 278 | 84.7k | if (end - begin <= static_cast<ptrdiff_t>(width)) return; | 279 | 41.0k | auto l = begin + width; | 280 | 41.0k | auto r = end; | 281 | 2.11M | while (l < r) { | 282 | 2.06M | if (comparator(begin, l)) { | 283 | 37.6k | r -= width; | 284 | 37.6k | swapper(l, r); | 285 | 2.03M | } else { | 286 | 2.03M | l += width; | 287 | 2.03M | } | 288 | 2.06M | } | 289 | 41.0k | l -= width; | 290 | 41.0k | swapper(begin, l); | 291 | 41.0k | SimpleQsort(begin, l, width, comparator, swapper); | 292 | 41.0k | SimpleQsort(r, end, width, comparator, swapper); | 293 | 41.0k | } |
idl_parser.cpp:void flatbuffers::(anonymous namespace)::SimpleQsort<flatbuffers::Offset<flatbuffers::Table>, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_4, void (*)(flatbuffers::Offset<flatbuffers::Table>*, flatbuffers::Offset<flatbuffers::Table>*)>(flatbuffers::Offset<flatbuffers::Table>*, flatbuffers::Offset<flatbuffers::Table>*, unsigned long, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_4, void (*)(flatbuffers::Offset<flatbuffers::Table>*, flatbuffers::Offset<flatbuffers::Table>*)) Line | Count | Source | 277 | 277k | S swapper) { | 278 | 277k | if (end - begin <= static_cast<ptrdiff_t>(width)) return; | 279 | 137k | auto l = begin + width; | 280 | 137k | auto r = end; | 281 | 153M | while (l < r) { | 282 | 153M | if (comparator(begin, l)) { | 283 | 36.2k | r -= width; | 284 | 36.2k | swapper(l, r); | 285 | 153M | } else { | 286 | 153M | l += width; | 287 | 153M | } | 288 | 153M | } | 289 | 137k | l -= width; | 290 | 137k | swapper(begin, l); | 291 | 137k | SimpleQsort(begin, l, width, comparator, swapper); | 292 | 137k | SimpleQsort(r, end, width, comparator, swapper); | 293 | 137k | } |
|
294 | | |
295 | | template <typename T> |
296 | 0 | static inline void SingleValueRepack(Value& e, T val) { |
297 | | // Remove leading zeros. |
298 | 0 | if (IsInteger(e.type.base_type)) { |
299 | 0 | e.constant = NumToString(val); |
300 | 0 | } |
301 | 0 | } Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<unsigned char>(flatbuffers::Value&, unsigned char) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<signed char>(flatbuffers::Value&, signed char) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<short>(flatbuffers::Value&, short) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<unsigned short>(flatbuffers::Value&, unsigned short) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<int>(flatbuffers::Value&, int) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<unsigned int>(flatbuffers::Value&, unsigned int) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<long>(flatbuffers::Value&, long) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::SingleValueRepack<unsigned long>(flatbuffers::Value&, unsigned long) |
302 | | |
303 | | #if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) |
304 | | // Normalize defaults NaN to unsigned quiet-NaN(0) if value was parsed from |
305 | | // hex-float literal. |
306 | 0 | static void SingleValueRepack(Value& e, float val) { |
307 | 0 | if (val != val) e.constant = "nan"; |
308 | 0 | } |
309 | 0 | static void SingleValueRepack(Value& e, double val) { |
310 | 0 | if (val != val) e.constant = "nan"; |
311 | 0 | } |
312 | | #endif |
313 | | |
314 | | template <typename T> |
315 | 0 | static uint64_t EnumDistanceImpl(T e1, T e2) { |
316 | 0 | if (e1 < e2) { |
317 | 0 | std::swap(e1, e2); |
318 | 0 | } // use std for scalars |
319 | | // Signed overflow may occur, use unsigned calculation. |
320 | | // The unsigned overflow is well-defined by C++ standard (modulo 2^n). |
321 | 0 | return static_cast<uint64_t>(e1) - static_cast<uint64_t>(e2); |
322 | 0 | } Unexecuted instantiation: idl_parser.cpp:unsigned long flatbuffers::(anonymous namespace)::EnumDistanceImpl<unsigned long>(unsigned long, unsigned long) Unexecuted instantiation: idl_parser.cpp:unsigned long flatbuffers::(anonymous namespace)::EnumDistanceImpl<long>(long, long) |
323 | | |
324 | 0 | static bool compareFieldDefs(const FieldDef* a, const FieldDef* b) { |
325 | 0 | auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str()); |
326 | 0 | auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str()); |
327 | 0 | return a_id < b_id; |
328 | 0 | } |
329 | | |
330 | | static Namespace* GetNamespace( |
331 | | const std::string& qualified_name, std::vector<Namespace*>& namespaces, |
332 | 46 | std::map<std::string, Namespace*>& namespaces_index) { |
333 | 46 | size_t dot = qualified_name.find_last_of('.'); |
334 | 46 | std::string namespace_name = (dot != std::string::npos) |
335 | 46 | ? std::string(qualified_name.c_str(), dot) |
336 | 46 | : ""; |
337 | 46 | Namespace*& ns = namespaces_index[namespace_name]; |
338 | | |
339 | 46 | if (!ns) { |
340 | 10 | ns = new Namespace(); |
341 | 10 | namespaces.push_back(ns); |
342 | | |
343 | 10 | size_t pos = 0; |
344 | | |
345 | 24 | for (;;) { |
346 | 24 | dot = qualified_name.find('.', pos); |
347 | 24 | if (dot == std::string::npos) { |
348 | 10 | break; |
349 | 10 | } |
350 | 14 | ns->components.push_back(qualified_name.substr(pos, dot - pos)); |
351 | 14 | pos = dot + 1; |
352 | 14 | } |
353 | 10 | } |
354 | | |
355 | 46 | return ns; |
356 | 46 | } |
357 | | |
358 | | // Generate a unique hash for a file based on its name and contents (if any). |
359 | 0 | static uint64_t HashFile(const char* source_filename, const char* source) { |
360 | 0 | uint64_t hash = 0; |
361 | |
|
362 | 0 | if (source_filename) |
363 | 0 | hash = HashFnv1a<uint64_t>(StripPath(source_filename).c_str()); |
364 | |
|
365 | 0 | if (source && *source) hash ^= HashFnv1a<uint64_t>(source); |
366 | |
|
367 | 0 | return hash; |
368 | 0 | } |
369 | | |
370 | | template <typename T> |
371 | 0 | static bool compareName(const T* a, const T* b) { |
372 | 0 | return a->defined_namespace->GetFullyQualifiedName(a->name) < |
373 | 0 | b->defined_namespace->GetFullyQualifiedName(b->name); |
374 | 0 | } Unexecuted instantiation: idl_parser.cpp:bool flatbuffers::(anonymous namespace)::compareName<flatbuffers::StructDef>(flatbuffers::StructDef const*, flatbuffers::StructDef const*) Unexecuted instantiation: idl_parser.cpp:bool flatbuffers::(anonymous namespace)::compareName<flatbuffers::EnumDef>(flatbuffers::EnumDef const*, flatbuffers::EnumDef const*) |
375 | | |
376 | | template <typename T> |
377 | 0 | static void AssignIndices(const std::vector<T*>& defvec) { |
378 | | // Pre-sort these vectors, such that we can set the correct indices for them. |
379 | 0 | auto vec = defvec; |
380 | 0 | std::sort(vec.begin(), vec.end(), compareName<T>); |
381 | 0 | for (int i = 0; i < static_cast<int>(vec.size()); i++) vec[i]->index = i; |
382 | 0 | } Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::AssignIndices<flatbuffers::StructDef>(std::__1::vector<flatbuffers::StructDef*, std::__1::allocator<flatbuffers::StructDef*> > const&) Unexecuted instantiation: idl_parser.cpp:void flatbuffers::(anonymous namespace)::AssignIndices<flatbuffers::EnumDef>(std::__1::vector<flatbuffers::EnumDef*, std::__1::allocator<flatbuffers::EnumDef*> > const&) |
383 | | |
384 | | } // namespace |
385 | | |
386 | 13.4k | void Parser::Message(const std::string& msg) { |
387 | 13.4k | if (!error_.empty()) error_ += "\n"; // log all warnings and errors |
388 | 13.4k | error_ += file_being_parsed_.length() ? AbsolutePath(file_being_parsed_) : ""; |
389 | | // clang-format off |
390 | | |
391 | | #ifdef _WIN32 // MSVC alike |
392 | | error_ += |
393 | | "(" + NumToString(line_) + ", " + NumToString(CursorPosition()) + ")"; |
394 | | #else // gcc alike |
395 | 13.4k | if (file_being_parsed_.length()) error_ += ":"; |
396 | 13.4k | error_ += NumToString(line_) + ": " + NumToString(CursorPosition()); |
397 | 13.4k | #endif |
398 | | // clang-format on |
399 | 13.4k | error_ += ": " + msg; |
400 | 13.4k | } |
401 | | |
402 | 0 | void Parser::Warning(const std::string& msg) { |
403 | 0 | if (!opts.no_warnings) { |
404 | 0 | Message("warning: " + msg); |
405 | 0 | has_warning_ = true; // for opts.warnings_as_errors |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | 13.4k | CheckedError Parser::Error(const std::string& msg) { |
410 | 13.4k | Message("error: " + msg); |
411 | 13.4k | return CheckedError(true); |
412 | 13.4k | } |
413 | | |
414 | 10 | CheckedError Parser::RecurseError() { |
415 | 10 | return Error("maximum parsing depth " + NumToString(parse_depth_counter_) + |
416 | 10 | " reached"); |
417 | 10 | } |
418 | | |
419 | 0 | const std::string& Parser::GetPooledString(const std::string& s) const { |
420 | 0 | return *(string_cache_.insert(s).first); |
421 | 0 | } |
422 | | |
423 | | class Parser::ParseDepthGuard { |
424 | | public: |
425 | | explicit ParseDepthGuard(Parser* parser_not_null) |
426 | 3.70M | : parser_(*parser_not_null), caller_depth_(parser_.parse_depth_counter_) { |
427 | 3.70M | FLATBUFFERS_ASSERT(caller_depth_ <= (FLATBUFFERS_MAX_PARSING_DEPTH) && |
428 | 3.70M | "Check() must be called to prevent stack overflow"); |
429 | 3.70M | parser_.parse_depth_counter_ += 1; |
430 | 3.70M | } |
431 | | |
432 | 3.70M | ~ParseDepthGuard() { parser_.parse_depth_counter_ -= 1; } |
433 | | |
434 | 3.70M | CheckedError Check() { |
435 | 3.70M | return caller_depth_ >= (FLATBUFFERS_MAX_PARSING_DEPTH) |
436 | 3.70M | ? parser_.RecurseError() |
437 | 3.70M | : CheckedError(false); |
438 | 3.70M | } |
439 | | |
440 | | FLATBUFFERS_DELETE_FUNC(ParseDepthGuard(const ParseDepthGuard&)); |
441 | | FLATBUFFERS_DELETE_FUNC(ParseDepthGuard& operator=(const ParseDepthGuard&)); |
442 | | |
443 | | private: |
444 | | Parser& parser_; |
445 | | const int caller_depth_; |
446 | | }; |
447 | | |
448 | | std::string Namespace::GetFullyQualifiedName(const std::string& name, |
449 | 4 | size_t max_components) const { |
450 | | // Early exit if we don't have a defined namespace. |
451 | 4 | if (components.empty() || !max_components) { |
452 | 0 | return name; |
453 | 0 | } |
454 | 4 | std::string stream_str; |
455 | 12 | for (size_t i = 0; i < std::min(components.size(), max_components); i++) { |
456 | 8 | stream_str += components[i]; |
457 | 8 | stream_str += '.'; |
458 | 8 | } |
459 | 4 | if (!stream_str.empty()) stream_str.pop_back(); |
460 | 4 | if (name.length()) { |
461 | 4 | stream_str += '.'; |
462 | 4 | stream_str += name; |
463 | 4 | } |
464 | 4 | return stream_str; |
465 | 4 | } |
466 | | |
467 | 5.36k | std::string Parser::TokenToStringId(int t) const { |
468 | 5.36k | return t == kTokenIdentifier ? attribute_ : TokenToString(t); |
469 | 5.36k | } |
470 | | |
471 | | // Parses exactly nibbles worth of hex digits into a number, or error. |
472 | 311k | CheckedError Parser::ParseHexNum(int nibbles, uint64_t* val) { |
473 | 311k | FLATBUFFERS_ASSERT(nibbles > 0); |
474 | 977k | for (int i = 0; i < nibbles; i++) |
475 | 665k | if (!is_xdigit(cursor_[i])) |
476 | 30 | return Error("escape code must be followed by " + NumToString(nibbles) + |
477 | 30 | " hex digits"); |
478 | 311k | std::string target(cursor_, cursor_ + nibbles); |
479 | 311k | *val = StringToUInt(target.c_str(), 16); |
480 | 311k | cursor_ += nibbles; |
481 | 311k | return NoError(); |
482 | 311k | } |
483 | | |
484 | 33.4k | CheckedError Parser::SkipByteOrderMark() { |
485 | 33.4k | if (static_cast<unsigned char>(*cursor_) != 0xef) return NoError(); |
486 | 22 | cursor_++; |
487 | 22 | if (static_cast<unsigned char>(*cursor_) != 0xbb) |
488 | 10 | return Error("invalid utf-8 byte order mark"); |
489 | 12 | cursor_++; |
490 | 12 | if (static_cast<unsigned char>(*cursor_) != 0xbf) |
491 | 11 | return Error("invalid utf-8 byte order mark"); |
492 | 1 | cursor_++; |
493 | 1 | return NoError(); |
494 | 12 | } |
495 | | |
496 | 10.4M | CheckedError Parser::Next() { |
497 | 10.4M | doc_comment_.clear(); |
498 | 10.4M | prev_cursor_ = cursor_; |
499 | 10.4M | bool seen_newline = cursor_ == source_; |
500 | 10.4M | attribute_.clear(); |
501 | 10.4M | attr_is_trivial_ascii_string_ = true; |
502 | 17.4M | for (;;) { |
503 | 17.4M | char c = *cursor_++; |
504 | 17.4M | token_ = c; |
505 | 17.4M | switch (c) { |
506 | 50.0k | case '\0': |
507 | 50.0k | cursor_--; |
508 | 50.0k | token_ = kTokenEof; |
509 | 50.0k | return NoError(); |
510 | 6.34M | case ' ': |
511 | 6.34M | case '\r': |
512 | 6.34M | case '\t': |
513 | 6.34M | break; |
514 | 678k | case '\n': |
515 | 678k | MarkNewLine(); |
516 | 678k | seen_newline = true; |
517 | 678k | break; |
518 | 403k | case '{': |
519 | 795k | case '}': |
520 | 798k | case '(': |
521 | 802k | case ')': |
522 | 1.90M | case '[': |
523 | 3.00M | case ']': |
524 | 3.00M | case '<': |
525 | 3.00M | case '>': |
526 | 5.94M | case ',': |
527 | 6.75M | case ':': |
528 | 6.75M | case ';': |
529 | 6.75M | case '=': |
530 | 6.75M | return NoError(); |
531 | 303k | case '\"': |
532 | 328k | case '\'': { |
533 | 328k | int unicode_high_surrogate = -1; |
534 | | |
535 | 3.89M | while (*cursor_ != c) { |
536 | 3.56M | if (*cursor_ < ' ' && static_cast<signed char>(*cursor_) >= 0) |
537 | 192 | return Error("illegal character in string constant"); |
538 | 3.56M | if (*cursor_ == '\\') { |
539 | 323k | attr_is_trivial_ascii_string_ = false; // has escape sequence |
540 | 323k | cursor_++; |
541 | 323k | if (unicode_high_surrogate != -1 && *cursor_ != 'u') { |
542 | 20 | return Error( |
543 | 20 | "illegal Unicode sequence (unpaired high surrogate)"); |
544 | 20 | } |
545 | 323k | switch (*cursor_) { |
546 | 571 | case 'n': |
547 | 571 | attribute_ += '\n'; |
548 | 571 | cursor_++; |
549 | 571 | break; |
550 | 2.04k | case 't': |
551 | 2.04k | attribute_ += '\t'; |
552 | 2.04k | cursor_++; |
553 | 2.04k | break; |
554 | 2.39k | case 'r': |
555 | 2.39k | attribute_ += '\r'; |
556 | 2.39k | cursor_++; |
557 | 2.39k | break; |
558 | 772 | case 'b': |
559 | 772 | attribute_ += '\b'; |
560 | 772 | cursor_++; |
561 | 772 | break; |
562 | 961 | case 'f': |
563 | 961 | attribute_ += '\f'; |
564 | 961 | cursor_++; |
565 | 961 | break; |
566 | 1.46k | case '\"': |
567 | 1.46k | attribute_ += '\"'; |
568 | 1.46k | cursor_++; |
569 | 1.46k | break; |
570 | 197 | case '\'': |
571 | 197 | attribute_ += '\''; |
572 | 197 | cursor_++; |
573 | 197 | break; |
574 | 3.17k | case '\\': |
575 | 3.17k | attribute_ += '\\'; |
576 | 3.17k | cursor_++; |
577 | 3.17k | break; |
578 | 212 | case '/': |
579 | 212 | attribute_ += '/'; |
580 | 212 | cursor_++; |
581 | 212 | break; |
582 | 290k | case 'x': { // Not in the JSON standard |
583 | 290k | cursor_++; |
584 | 290k | uint64_t val; |
585 | 290k | ECHECK(ParseHexNum(2, &val)); |
586 | 290k | attribute_ += static_cast<char>(val); |
587 | 290k | break; |
588 | 290k | } |
589 | 20.8k | case 'u': { |
590 | 20.8k | cursor_++; |
591 | 20.8k | uint64_t val; |
592 | 20.8k | ECHECK(ParseHexNum(4, &val)); |
593 | 20.8k | if (val >= 0xD800 && val <= 0xDBFF) { |
594 | 1.52k | if (unicode_high_surrogate != -1) { |
595 | 1 | return Error( |
596 | 1 | "illegal Unicode sequence (multiple high surrogates)"); |
597 | 1.52k | } else { |
598 | 1.52k | unicode_high_surrogate = static_cast<int>(val); |
599 | 1.52k | } |
600 | 19.2k | } else if (val >= 0xDC00 && val <= 0xDFFF) { |
601 | 1.48k | if (unicode_high_surrogate == -1) { |
602 | 13 | return Error( |
603 | 13 | "illegal Unicode sequence (unpaired low surrogate)"); |
604 | 1.46k | } else { |
605 | 1.46k | int code_point = 0x10000 + |
606 | 1.46k | ((unicode_high_surrogate & 0x03FF) << 10) + |
607 | 1.46k | (val & 0x03FF); |
608 | 1.46k | ToUTF8(code_point, &attribute_); |
609 | 1.46k | unicode_high_surrogate = -1; |
610 | 1.46k | } |
611 | 17.8k | } else { |
612 | 17.8k | if (unicode_high_surrogate != -1) { |
613 | 1 | return Error( |
614 | 1 | "illegal Unicode sequence (unpaired high surrogate)"); |
615 | 1 | } |
616 | 17.8k | ToUTF8(static_cast<int>(val), &attribute_); |
617 | 17.8k | } |
618 | 20.8k | break; |
619 | 20.8k | } |
620 | 20.8k | default: |
621 | 5 | return Error("unknown escape code in string constant"); |
622 | 323k | } |
623 | 3.24M | } else { // printable chars + UTF-8 bytes |
624 | 3.24M | if (unicode_high_surrogate != -1) { |
625 | 11 | return Error( |
626 | 11 | "illegal Unicode sequence (unpaired high surrogate)"); |
627 | 11 | } |
628 | | // reset if non-printable |
629 | 3.24M | attr_is_trivial_ascii_string_ &= |
630 | 3.24M | check_ascii_range(*cursor_, ' ', '~'); |
631 | | |
632 | 3.24M | attribute_ += *cursor_++; |
633 | 3.24M | } |
634 | 3.56M | } |
635 | 328k | if (unicode_high_surrogate != -1) { |
636 | 11 | return Error("illegal Unicode sequence (unpaired high surrogate)"); |
637 | 11 | } |
638 | 328k | cursor_++; |
639 | 328k | if (!attr_is_trivial_ascii_string_ && !opts.allow_non_utf8 && |
640 | 3.73k | !ValidateUTF8(attribute_)) { |
641 | 83 | return Error("illegal UTF-8 sequence"); |
642 | 83 | } |
643 | 328k | token_ = kTokenStringConstant; |
644 | 328k | return NoError(); |
645 | 328k | } |
646 | 50.3k | case '/': |
647 | 50.3k | if (*cursor_ == '/') { |
648 | 50.0k | const char* start = ++cursor_; |
649 | 409k | while (*cursor_ && *cursor_ != '\n' && *cursor_ != '\r') cursor_++; |
650 | 50.0k | if (*start == '/') { // documentation comment |
651 | 31.8k | if (!seen_newline) |
652 | 1 | return Error( |
653 | 1 | "a documentation comment should be on a line on its own"); |
654 | 31.8k | doc_comment_.push_back(std::string(start + 1, cursor_)); |
655 | 31.8k | } |
656 | 50.0k | break; |
657 | 50.0k | } else if (*cursor_ == '*') { |
658 | 239 | cursor_++; |
659 | | // TODO: make nested. |
660 | 1.03k | while (*cursor_ != '*' || cursor_[1] != '/') { |
661 | 839 | if (*cursor_ == '\n') MarkNewLine(); |
662 | 839 | if (!*cursor_) return Error("end of file in comment"); |
663 | 798 | cursor_++; |
664 | 798 | } |
665 | 198 | cursor_ += 2; |
666 | 198 | break; |
667 | 239 | } |
668 | 17 | FLATBUFFERS_FALLTHROUGH(); // else fall thru |
669 | 3.28M | default: |
670 | 3.28M | if (IsIdentifierStart(c)) { |
671 | | // Collect all chars of an identifier: |
672 | 825k | const char* start = cursor_ - 1; |
673 | 4.86M | while (IsIdentifierStart(*cursor_) || is_digit(*cursor_)) cursor_++; |
674 | 825k | attribute_.append(start, cursor_); |
675 | 825k | token_ = kTokenIdentifier; |
676 | 825k | return NoError(); |
677 | 825k | } |
678 | | |
679 | 2.45M | const auto has_sign = (c == '+') || (c == '-'); |
680 | 2.45M | if (has_sign) { |
681 | | // Check for +/-inf which is considered a float constant. |
682 | 20.3k | if (strncmp(cursor_, "inf", 3) == 0 && |
683 | 1.41k | !(IsIdentifierStart(cursor_[3]) || is_digit(cursor_[3]))) { |
684 | 938 | attribute_.assign(cursor_ - 1, cursor_ + 3); |
685 | 938 | token_ = kTokenFloatConstant; |
686 | 938 | cursor_ += 3; |
687 | 938 | return NoError(); |
688 | 938 | } |
689 | | |
690 | 19.4k | if (IsIdentifierStart(*cursor_)) { |
691 | | // '-'/'+' and following identifier - it could be a predefined |
692 | | // constant. Return the sign in token_, see ParseSingleValue. |
693 | 2.95k | return NoError(); |
694 | 2.95k | } |
695 | 19.4k | } |
696 | | |
697 | 2.45M | auto dot_lvl = |
698 | 2.45M | (c == '.') ? 0 : 1; // dot_lvl==0 <=> exactly one '.' seen |
699 | 2.45M | if (!dot_lvl && !is_digit(*cursor_)) return NoError(); // enum? |
700 | | // Parser accepts hexadecimal-floating-literal (see C++ 5.13.4). |
701 | 2.45M | if (is_digit(c) || has_sign || !dot_lvl) { |
702 | 2.45M | const auto start = cursor_ - 1; |
703 | 2.45M | auto start_digits = !is_digit(c) ? cursor_ : cursor_ - 1; |
704 | 2.45M | if (!is_digit(c) && is_digit(*cursor_)) { |
705 | 546k | start_digits = cursor_; // see digit in cursor_ position |
706 | 546k | c = *cursor_++; |
707 | 546k | } |
708 | | // hex-float can't begind with '.' |
709 | 2.45M | auto use_hex = dot_lvl && (c == '0') && is_alpha_char(*cursor_, 'X'); |
710 | 2.45M | if (use_hex) start_digits = ++cursor_; // '0x' is the prefix, skip it |
711 | | // Read an integer number or mantisa of float-point number. |
712 | 3.01M | do { |
713 | 3.01M | if (use_hex) { |
714 | 219k | while (is_xdigit(*cursor_)) cursor_++; |
715 | 3.01M | } else { |
716 | 15.2M | while (is_digit(*cursor_)) cursor_++; |
717 | 3.01M | } |
718 | 3.01M | } while ((*cursor_ == '.') && (++cursor_) && (--dot_lvl >= 0)); |
719 | | // Exponent of float-point number. |
720 | 2.45M | if ((dot_lvl >= 0) && (cursor_ > start_digits)) { |
721 | | // The exponent suffix of hexadecimal float number is mandatory. |
722 | 2.45M | if (use_hex && !dot_lvl) start_digits = cursor_; |
723 | 2.45M | if ((use_hex && is_alpha_char(*cursor_, 'P')) || |
724 | 2.45M | is_alpha_char(*cursor_, 'E')) { |
725 | 41.5k | dot_lvl = 0; // Emulate dot to signal about float-point number. |
726 | 41.5k | cursor_++; |
727 | 41.5k | if (*cursor_ == '+' || *cursor_ == '-') cursor_++; |
728 | 41.5k | start_digits = cursor_; // the exponent-part has to have digits |
729 | | // Exponent is decimal integer number |
730 | 162k | while (is_digit(*cursor_)) cursor_++; |
731 | 41.5k | if (*cursor_ == '.') { |
732 | 1 | cursor_++; // If see a dot treat it as part of invalid number. |
733 | 1 | dot_lvl = -1; // Fall thru to Error(). |
734 | 1 | } |
735 | 41.5k | } |
736 | 2.45M | } |
737 | | // Finalize. |
738 | 2.45M | if ((dot_lvl >= 0) && (cursor_ > start_digits)) { |
739 | 2.45M | attribute_.append(start, cursor_); |
740 | 2.45M | token_ = dot_lvl ? kTokenIntegerConstant : kTokenFloatConstant; |
741 | 2.45M | return NoError(); |
742 | 2.45M | } else { |
743 | 127 | return Error("invalid number: " + std::string(start, cursor_)); |
744 | 127 | } |
745 | 2.45M | } |
746 | 163 | std::string ch; |
747 | 163 | ch = c; |
748 | 163 | if (false == check_ascii_range(c, ' ', '~')) |
749 | 108 | ch = "code: " + NumToString(c); |
750 | 163 | return Error("illegal character: " + ch); |
751 | 17.4M | } |
752 | 17.4M | } |
753 | 10.4M | } |
754 | | |
755 | | // Check if a given token is next. |
756 | 9.78M | bool Parser::Is(int t) const { return t == token_; } |
757 | | |
758 | 397k | bool Parser::IsIdent(const char* id) const { |
759 | 397k | return token_ == kTokenIdentifier && attribute_ == id; |
760 | 397k | } |
761 | | |
762 | | // Expect a given token to be next, consume it, or error if not present. |
763 | 7.62M | CheckedError Parser::Expect(int t) { |
764 | 7.62M | if (t != token_) { |
765 | 4.66k | return Error("expecting: " + TokenToString(t) + |
766 | 4.66k | " instead got: " + TokenToStringId(token_)); |
767 | 4.66k | } |
768 | 7.61M | NEXT(); |
769 | 7.61M | return NoError(); |
770 | 7.61M | } |
771 | | |
772 | 0 | CheckedError Parser::ParseNamespacing(std::string* id, std::string* last) { |
773 | 0 | while (Is('.')) { |
774 | 0 | NEXT(); |
775 | 0 | *id += "."; |
776 | 0 | *id += attribute_; |
777 | 0 | if (last) *last = attribute_; |
778 | 0 | EXPECT(kTokenIdentifier); |
779 | 0 | } |
780 | 0 | return NoError(); |
781 | 0 | } |
782 | | |
783 | 4.76k | EnumDef* Parser::LookupEnum(const std::string& id) { |
784 | | // Search thru parent namespaces. |
785 | 4.76k | return LookupTableByName(enums_, id, *current_namespace_, 0); |
786 | 4.76k | } |
787 | | |
788 | 4 | StructDef* Parser::LookupStruct(const std::string& id) const { |
789 | 4 | auto sd = structs_.Lookup(id); |
790 | 4 | if (sd) sd->refcount++; |
791 | 4 | return sd; |
792 | 4 | } |
793 | | |
794 | | StructDef* Parser::LookupStructThruParentNamespaces( |
795 | 0 | const std::string& id) const { |
796 | 0 | auto sd = LookupTableByName(structs_, id, *current_namespace_, 1); |
797 | 0 | if (sd) sd->refcount++; |
798 | 0 | return sd; |
799 | 0 | } |
800 | | |
801 | 0 | CheckedError Parser::ParseTypeIdent(Type& type) { |
802 | 0 | std::string id = attribute_; |
803 | 0 | EXPECT(kTokenIdentifier); |
804 | 0 | ECHECK(ParseNamespacing(&id, nullptr)); |
805 | 0 | auto enum_def = LookupEnum(id); |
806 | 0 | if (enum_def) { |
807 | 0 | type = enum_def->underlying_type; |
808 | 0 | if (enum_def->is_union) type.base_type = BASE_TYPE_UNION; |
809 | 0 | } else { |
810 | 0 | type.base_type = BASE_TYPE_STRUCT; |
811 | 0 | type.struct_def = LookupCreateStruct(id); |
812 | 0 | } |
813 | 0 | return NoError(); |
814 | 0 | } |
815 | | |
816 | | // Parse any IDL type. |
817 | 0 | CheckedError Parser::ParseType(Type& type) { |
818 | 0 | if (token_ == kTokenIdentifier) { |
819 | 0 | if (IsIdent("bool")) { |
820 | 0 | type.base_type = BASE_TYPE_BOOL; |
821 | 0 | NEXT(); |
822 | 0 | } else if (IsIdent("byte") || IsIdent("int8")) { |
823 | 0 | type.base_type = BASE_TYPE_CHAR; |
824 | 0 | NEXT(); |
825 | 0 | } else if (IsIdent("ubyte") || IsIdent("uint8")) { |
826 | 0 | type.base_type = BASE_TYPE_UCHAR; |
827 | 0 | NEXT(); |
828 | 0 | } else if (IsIdent("short") || IsIdent("int16")) { |
829 | 0 | type.base_type = BASE_TYPE_SHORT; |
830 | 0 | NEXT(); |
831 | 0 | } else if (IsIdent("ushort") || IsIdent("uint16")) { |
832 | 0 | type.base_type = BASE_TYPE_USHORT; |
833 | 0 | NEXT(); |
834 | 0 | } else if (IsIdent("int") || IsIdent("int32")) { |
835 | 0 | type.base_type = BASE_TYPE_INT; |
836 | 0 | NEXT(); |
837 | 0 | } else if (IsIdent("uint") || IsIdent("uint32")) { |
838 | 0 | type.base_type = BASE_TYPE_UINT; |
839 | 0 | NEXT(); |
840 | 0 | } else if (IsIdent("long") || IsIdent("int64")) { |
841 | 0 | type.base_type = BASE_TYPE_LONG; |
842 | 0 | NEXT(); |
843 | 0 | } else if (IsIdent("ulong") || IsIdent("uint64")) { |
844 | 0 | type.base_type = BASE_TYPE_ULONG; |
845 | 0 | NEXT(); |
846 | 0 | } else if (IsIdent("float") || IsIdent("float32")) { |
847 | 0 | type.base_type = BASE_TYPE_FLOAT; |
848 | 0 | NEXT(); |
849 | 0 | } else if (IsIdent("double") || IsIdent("float64")) { |
850 | 0 | type.base_type = BASE_TYPE_DOUBLE; |
851 | 0 | NEXT(); |
852 | 0 | } else if (IsIdent("string")) { |
853 | 0 | type.base_type = BASE_TYPE_STRING; |
854 | 0 | NEXT(); |
855 | 0 | } else { |
856 | 0 | ECHECK(ParseTypeIdent(type)); |
857 | 0 | } |
858 | 0 | } else if (token_ == '[') { |
859 | 0 | ParseDepthGuard depth_guard(this); |
860 | 0 | ECHECK(depth_guard.Check()); |
861 | 0 | NEXT(); |
862 | 0 | Type subtype; |
863 | 0 | ECHECK(ParseType(subtype)); |
864 | 0 | if (IsSeries(subtype)) { |
865 | | // We could support this, but it will complicate things, and it's |
866 | | // easier to work around with a struct around the inner vector. |
867 | 0 | return Error("nested vector types not supported (wrap in table first)"); |
868 | 0 | } |
869 | 0 | if (token_ == ':') { |
870 | 0 | NEXT(); |
871 | 0 | if (token_ != kTokenIntegerConstant) { |
872 | 0 | return Error("length of fixed-length array must be an integer value"); |
873 | 0 | } |
874 | 0 | uint16_t fixed_length = 0; |
875 | 0 | bool check = StringToNumber(attribute_.c_str(), &fixed_length); |
876 | 0 | if (!check || fixed_length < 1) { |
877 | 0 | return Error( |
878 | 0 | "length of fixed-length array must be positive and fit to " |
879 | 0 | "uint16_t type"); |
880 | 0 | } |
881 | 0 | type = Type(BASE_TYPE_ARRAY, subtype.struct_def, subtype.enum_def, |
882 | 0 | fixed_length); |
883 | 0 | NEXT(); |
884 | 0 | } else { |
885 | 0 | type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def); |
886 | 0 | } |
887 | 0 | type.element = subtype.base_type; |
888 | 0 | EXPECT(']'); |
889 | 0 | } else { |
890 | 0 | return Error("illegal type syntax"); |
891 | 0 | } |
892 | 0 | return NoError(); |
893 | 0 | } |
894 | | |
895 | | CheckedError Parser::AddField(StructDef& struct_def, const std::string& name, |
896 | 0 | const Type& type, FieldDef** dest) { |
897 | 0 | auto& field = *new FieldDef(); |
898 | 0 | field.value.offset = |
899 | 0 | FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size())); |
900 | 0 | field.name = name; |
901 | 0 | field.file = struct_def.file; |
902 | 0 | field.value.type = type; |
903 | 0 | if (struct_def.fixed) { // statically compute the field offset |
904 | 0 | auto size = InlineSize(type); |
905 | 0 | auto alignment = InlineAlignment(type); |
906 | | // structs_ need to have a predictable format, so we need to align to |
907 | | // the largest scalar |
908 | 0 | struct_def.minalign = std::max(struct_def.minalign, alignment); |
909 | 0 | struct_def.PadLastField(alignment); |
910 | 0 | field.value.offset = static_cast<voffset_t>(struct_def.bytesize); |
911 | 0 | struct_def.bytesize += size; |
912 | 0 | } |
913 | 0 | if (struct_def.fields.Add(name, &field)) |
914 | 0 | return Error("field already exists: " + name); |
915 | 0 | *dest = &field; |
916 | 0 | return NoError(); |
917 | 0 | } |
918 | | |
919 | 0 | CheckedError Parser::ParseField(StructDef& struct_def) { |
920 | 0 | std::string name = attribute_; |
921 | |
|
922 | 0 | if (LookupCreateStruct(name, false, false)) |
923 | 0 | return Error("field name can not be the same as table/struct name"); |
924 | | |
925 | 0 | if (!IsLowerSnakeCase(name)) { |
926 | 0 | Warning("field names should be lowercase snake_case, got: " + name); |
927 | 0 | } |
928 | |
|
929 | 0 | std::vector<std::string> dc = doc_comment_; |
930 | 0 | EXPECT(kTokenIdentifier); |
931 | 0 | EXPECT(':'); |
932 | 0 | Type type; |
933 | 0 | ECHECK(ParseType(type)); |
934 | |
|
935 | 0 | if (struct_def.fixed) { |
936 | 0 | if (IsIncompleteStruct(type) || |
937 | 0 | (IsArray(type) && IsIncompleteStruct(type.VectorType()))) { |
938 | 0 | std::string type_name = IsArray(type) ? type.VectorType().struct_def->name |
939 | 0 | : type.struct_def->name; |
940 | 0 | return Error( |
941 | 0 | std::string("Incomplete type in struct is not allowed, type name: ") + |
942 | 0 | type_name); |
943 | 0 | } |
944 | | |
945 | 0 | auto valid = IsScalar(type.base_type) || IsStruct(type); |
946 | 0 | if (!valid && IsArray(type)) { |
947 | 0 | const auto& elem_type = type.VectorType(); |
948 | 0 | valid |= IsScalar(elem_type.base_type) || IsStruct(elem_type); |
949 | 0 | } |
950 | 0 | if (!valid) |
951 | 0 | return Error("structs may contain only scalar or struct fields"); |
952 | 0 | } |
953 | | |
954 | 0 | if (!struct_def.fixed && IsArray(type)) |
955 | 0 | return Error("fixed-length array in table must be wrapped in struct"); |
956 | | |
957 | 0 | if (IsArray(type)) { |
958 | 0 | advanced_features_ |= reflection::AdvancedArrayFeatures; |
959 | 0 | if (!SupportsAdvancedArrayFeatures()) { |
960 | 0 | return Error( |
961 | 0 | "Arrays are not yet supported in all " |
962 | 0 | "the specified programming languages."); |
963 | 0 | } |
964 | 0 | } |
965 | | |
966 | 0 | FieldDef* typefield = nullptr; |
967 | 0 | if (type.base_type == BASE_TYPE_UNION) { |
968 | | // For union fields, add a second auto-generated field to hold the type, |
969 | | // with a special suffix. |
970 | | |
971 | | // To ensure compatibility with many codes that rely on the BASE_TYPE_UTYPE |
972 | | // value to identify union type fields. |
973 | 0 | Type union_type(type.enum_def->underlying_type); |
974 | 0 | union_type.base_type = BASE_TYPE_UTYPE; |
975 | 0 | ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(), union_type, |
976 | 0 | &typefield)); |
977 | |
|
978 | 0 | } else if (IsVector(type) && type.element == BASE_TYPE_UNION) { |
979 | 0 | advanced_features_ |= reflection::AdvancedUnionFeatures; |
980 | | // Only cpp, js and ts supports the union vector feature so far. |
981 | 0 | if (!SupportsAdvancedUnionFeatures()) { |
982 | 0 | return Error( |
983 | 0 | "Vectors of unions are not yet supported in at least one of " |
984 | 0 | "the specified programming languages."); |
985 | 0 | } |
986 | | // For vector of union fields, add a second auto-generated vector field to |
987 | | // hold the types, with a special suffix. |
988 | 0 | Type union_vector(BASE_TYPE_VECTOR, nullptr, type.enum_def); |
989 | 0 | union_vector.element = BASE_TYPE_UTYPE; |
990 | 0 | ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(), union_vector, |
991 | 0 | &typefield)); |
992 | 0 | } |
993 | | |
994 | 0 | FieldDef* field; |
995 | 0 | ECHECK(AddField(struct_def, name, type, &field)); |
996 | |
|
997 | 0 | if (typefield) { |
998 | | // We preserve the relation between the typefield |
999 | | // and field, so we can easily map it in the code |
1000 | | // generators. |
1001 | 0 | typefield->sibling_union_field = field; |
1002 | 0 | field->sibling_union_field = typefield; |
1003 | 0 | } |
1004 | |
|
1005 | 0 | if (token_ == '=') { |
1006 | 0 | NEXT(); |
1007 | 0 | ECHECK(ParseSingleValue(&field->name, field->value, true)); |
1008 | 0 | if (IsStruct(type) || (struct_def.fixed && field->value.constant != "0")) |
1009 | 0 | return Error( |
1010 | 0 | "default values are not supported for struct fields, table fields, " |
1011 | 0 | "or in structs."); |
1012 | 0 | if (IsString(type) || IsVector(type)) { |
1013 | 0 | advanced_features_ |= reflection::DefaultVectorsAndStrings; |
1014 | 0 | if (field->value.constant != "0" && !SupportsDefaultVectorsAndStrings()) { |
1015 | 0 | return Error( |
1016 | 0 | "Default values for strings and vectors are not supported in one " |
1017 | 0 | "of the specified programming languages"); |
1018 | 0 | } |
1019 | 0 | } |
1020 | | |
1021 | 0 | if (IsVector(type) && field->value.constant != "0" && |
1022 | 0 | field->value.constant != "[]") { |
1023 | 0 | return Error("The only supported default for vectors is `[]`."); |
1024 | 0 | } |
1025 | 0 | } |
1026 | | |
1027 | | // Append .0 if the value has not it (skip hex and scientific floats). |
1028 | | // This suffix needed for generated C++ code. |
1029 | 0 | if (IsFloat(type.base_type)) { |
1030 | 0 | auto& text = field->value.constant; |
1031 | 0 | FLATBUFFERS_ASSERT(false == text.empty()); |
1032 | 0 | auto s = text.c_str(); |
1033 | 0 | while (*s == ' ') s++; |
1034 | 0 | if (*s == '-' || *s == '+') s++; |
1035 | | // 1) A float constants (nan, inf, pi, etc) is a kind of identifier. |
1036 | | // 2) A float number needn't ".0" at the end if it has exponent. |
1037 | 0 | if ((false == IsIdentifierStart(*s)) && |
1038 | 0 | (std::string::npos == field->value.constant.find_first_of(".eEpP"))) { |
1039 | 0 | field->value.constant += ".0"; |
1040 | 0 | } |
1041 | 0 | } |
1042 | | |
1043 | 0 | field->doc_comment = dc; |
1044 | 0 | ECHECK(ParseMetaData(&field->attributes)); |
1045 | 0 | field->deprecated = field->attributes.Lookup("deprecated") != nullptr; |
1046 | 0 | auto hash_name = field->attributes.Lookup("hash"); |
1047 | 0 | if (hash_name) { |
1048 | 0 | switch ((IsVector(type)) ? type.element : type.base_type) { |
1049 | 0 | case BASE_TYPE_SHORT: |
1050 | 0 | case BASE_TYPE_USHORT: { |
1051 | 0 | if (FindHashFunction16(hash_name->constant.c_str()) == nullptr) |
1052 | 0 | return Error("Unknown hashing algorithm for 16 bit types: " + |
1053 | 0 | hash_name->constant); |
1054 | 0 | break; |
1055 | 0 | } |
1056 | 0 | case BASE_TYPE_INT: |
1057 | 0 | case BASE_TYPE_UINT: { |
1058 | 0 | if (FindHashFunction32(hash_name->constant.c_str()) == nullptr) |
1059 | 0 | return Error("Unknown hashing algorithm for 32 bit types: " + |
1060 | 0 | hash_name->constant); |
1061 | 0 | break; |
1062 | 0 | } |
1063 | 0 | case BASE_TYPE_LONG: |
1064 | 0 | case BASE_TYPE_ULONG: { |
1065 | 0 | if (FindHashFunction64(hash_name->constant.c_str()) == nullptr) |
1066 | 0 | return Error("Unknown hashing algorithm for 64 bit types: " + |
1067 | 0 | hash_name->constant); |
1068 | 0 | break; |
1069 | 0 | } |
1070 | 0 | default: |
1071 | 0 | return Error( |
1072 | 0 | "only short, ushort, int, uint, long and ulong data types support " |
1073 | 0 | "hashing."); |
1074 | 0 | } |
1075 | 0 | } |
1076 | | |
1077 | 0 | if (field->attributes.Lookup("vector64") != nullptr) { |
1078 | 0 | if (!IsVector(type)) { |
1079 | 0 | return Error("`vector64` attribute can only be applied on vectors."); |
1080 | 0 | } |
1081 | | |
1082 | | // Upgrade the type to be a BASE_TYPE_VECTOR64, since the attributes are |
1083 | | // parsed after the type. |
1084 | 0 | const BaseType element_base_type = type.element; |
1085 | 0 | type = Type(BASE_TYPE_VECTOR64, type.struct_def, type.enum_def); |
1086 | 0 | type.element = element_base_type; |
1087 | | |
1088 | | // Since the field was already added to the parent object, update the type |
1089 | | // in place. |
1090 | 0 | field->value.type = type; |
1091 | | |
1092 | | // 64-bit vectors imply the offset64 attribute. |
1093 | 0 | field->offset64 = true; |
1094 | 0 | } |
1095 | | |
1096 | | // Record that this field uses 64-bit offsets. |
1097 | 0 | if (field->attributes.Lookup("offset64") != nullptr) { |
1098 | | // TODO(derekbailey): would be nice to have this be a recommendation or hint |
1099 | | // instead of a warning. |
1100 | 0 | if (type.base_type == BASE_TYPE_VECTOR64) { |
1101 | 0 | Warning("attribute `vector64` implies `offset64` and isn't required."); |
1102 | 0 | } |
1103 | |
|
1104 | 0 | field->offset64 = true; |
1105 | 0 | } |
1106 | | |
1107 | | // Check for common conditions with Offset64 fields. |
1108 | 0 | if (field->offset64) { |
1109 | | // TODO(derekbailey): this is where we can disable string support for |
1110 | | // offset64, as that is not a hard requirement to have. |
1111 | 0 | if (!IsString(type) && !IsVector(type)) { |
1112 | 0 | return Error( |
1113 | 0 | "only string and vectors can have `offset64` attribute applied"); |
1114 | 0 | } |
1115 | | |
1116 | | // If this is a Vector, only scalar and scalar-like (structs) items are |
1117 | | // allowed. |
1118 | | // TODO(derekbailey): allow vector of strings, just require that the strings |
1119 | | // are Offset64<string>. |
1120 | 0 | if (IsVector(type) && |
1121 | 0 | !((IsScalar(type.element) && !IsEnum(type.VectorType())) || |
1122 | 0 | IsStruct(type.VectorType()))) { |
1123 | 0 | return Error("only vectors of scalars are allowed to be 64-bit."); |
1124 | 0 | } |
1125 | | |
1126 | | // Lastly, check if it is supported by the specified generated languages. Do |
1127 | | // this last so the above checks can inform the user of schema errors to fix |
1128 | | // first. |
1129 | 0 | if (!Supports64BitOffsets()) { |
1130 | 0 | return Error( |
1131 | 0 | "fields using 64-bit offsets are not yet supported in at least one " |
1132 | 0 | "of the specified programming languages."); |
1133 | 0 | } |
1134 | 0 | } |
1135 | | |
1136 | | // For historical convenience reasons, string keys are assumed required. |
1137 | | // Scalars are kDefault unless otherwise specified. |
1138 | | // Nonscalars are kOptional unless required; |
1139 | 0 | field->key = field->attributes.Lookup("key") != nullptr; |
1140 | 0 | const bool required = field->attributes.Lookup("required") != nullptr || |
1141 | 0 | (IsString(type) && field->key); |
1142 | 0 | const bool default_str_or_vec = |
1143 | 0 | ((IsString(type) || IsVector(type)) && field->value.constant != "0"); |
1144 | 0 | const bool optional = IsScalar(type.base_type) |
1145 | 0 | ? (field->value.constant == "null") |
1146 | 0 | : !(required || default_str_or_vec); |
1147 | 0 | if (required && optional) { |
1148 | 0 | return Error("Fields cannot be both optional and required."); |
1149 | 0 | } |
1150 | 0 | field->presence = FieldDef::MakeFieldPresence(optional, required); |
1151 | |
|
1152 | 0 | if (required && (struct_def.fixed || IsScalar(type.base_type))) { |
1153 | 0 | return Error("only non-scalar fields in tables may be 'required'"); |
1154 | 0 | } |
1155 | 0 | if (field->key) { |
1156 | 0 | if (struct_def.has_key) return Error("only one field may be set as 'key'"); |
1157 | 0 | struct_def.has_key = true; |
1158 | 0 | auto is_valid = |
1159 | 0 | IsScalar(type.base_type) || IsString(type) || IsStruct(type); |
1160 | 0 | if (IsArray(type)) { |
1161 | 0 | is_valid |= |
1162 | 0 | IsScalar(type.VectorType().base_type) || IsStruct(type.VectorType()); |
1163 | 0 | } |
1164 | 0 | if (!is_valid) { |
1165 | 0 | return Error( |
1166 | 0 | "'key' field must be string, scalar type or fixed size array of " |
1167 | 0 | "scalars"); |
1168 | 0 | } |
1169 | 0 | } |
1170 | | |
1171 | 0 | if (field->IsScalarOptional()) { |
1172 | 0 | advanced_features_ |= reflection::OptionalScalars; |
1173 | 0 | if (type.enum_def && type.enum_def->Lookup("null")) { |
1174 | 0 | FLATBUFFERS_ASSERT(IsInteger(type.base_type)); |
1175 | 0 | return Error( |
1176 | 0 | "the default 'null' is reserved for declaring optional scalar " |
1177 | 0 | "fields, it conflicts with declaration of enum '" + |
1178 | 0 | type.enum_def->name + "'."); |
1179 | 0 | } |
1180 | 0 | if (field->attributes.Lookup("key")) { |
1181 | 0 | return Error( |
1182 | 0 | "only a non-optional scalar field can be used as a 'key' field"); |
1183 | 0 | } |
1184 | 0 | if (!SupportsOptionalScalars()) { |
1185 | 0 | return Error( |
1186 | 0 | "Optional scalars are not yet supported in at least one of " |
1187 | 0 | "the specified programming languages."); |
1188 | 0 | } |
1189 | 0 | } |
1190 | | |
1191 | 0 | if (type.enum_def) { |
1192 | | // Verify the enum's type and default value. |
1193 | 0 | const std::string& constant = field->value.constant; |
1194 | 0 | if (type.base_type == BASE_TYPE_UNION) { |
1195 | 0 | if (constant != "0") { |
1196 | 0 | return Error("Union defaults must be NONE"); |
1197 | 0 | } |
1198 | 0 | } else if (IsVector(type)) { |
1199 | 0 | if (constant != "0" && constant != "[]") { |
1200 | 0 | return Error("Vector defaults may only be `[]`."); |
1201 | 0 | } |
1202 | 0 | } else if (IsArray(type)) { |
1203 | 0 | if (constant != "0") { |
1204 | 0 | return Error("Array defaults are not supported yet."); |
1205 | 0 | } |
1206 | 0 | } else { |
1207 | 0 | if (!IsInteger(type.base_type)) { |
1208 | 0 | return Error("Enums must have integer base types"); |
1209 | 0 | } |
1210 | | // Optional and bitflags enums may have default constants that are not |
1211 | | // their specified variants. |
1212 | 0 | if (!field->IsOptional() && |
1213 | 0 | type.enum_def->attributes.Lookup("bit_flags") == nullptr) { |
1214 | 0 | if (type.enum_def->FindByValue(constant) == nullptr) { |
1215 | 0 | return Error("default value of `" + constant + "` for " + "field `" + |
1216 | 0 | name + "` is not part of enum `" + type.enum_def->name + |
1217 | 0 | "`."); |
1218 | 0 | } |
1219 | 0 | } |
1220 | 0 | } |
1221 | 0 | } |
1222 | | |
1223 | 0 | if (field->deprecated && struct_def.fixed) |
1224 | 0 | return Error("can't deprecate fields in a struct"); |
1225 | | |
1226 | 0 | auto cpp_type = field->attributes.Lookup("cpp_type"); |
1227 | 0 | if (cpp_type) { |
1228 | 0 | if (!hash_name) |
1229 | 0 | return Error("cpp_type can only be used with a hashed field"); |
1230 | | /// forcing cpp_ptr_type to 'naked' if unset |
1231 | 0 | auto cpp_ptr_type = field->attributes.Lookup("cpp_ptr_type"); |
1232 | 0 | if (!cpp_ptr_type) { |
1233 | 0 | auto val = new Value(); |
1234 | 0 | val->type = cpp_type->type; |
1235 | 0 | val->constant = "naked"; |
1236 | 0 | field->attributes.Add("cpp_ptr_type", val); |
1237 | 0 | } |
1238 | 0 | } |
1239 | | |
1240 | 0 | field->shared = field->attributes.Lookup("shared") != nullptr; |
1241 | 0 | if (field->shared && field->value.type.base_type != BASE_TYPE_STRING) |
1242 | 0 | return Error("shared can only be defined on strings"); |
1243 | | |
1244 | 0 | auto field_native_custom_alloc = |
1245 | 0 | field->attributes.Lookup("native_custom_alloc"); |
1246 | 0 | if (field_native_custom_alloc) |
1247 | 0 | return Error( |
1248 | 0 | "native_custom_alloc can only be used with a table or struct " |
1249 | 0 | "definition"); |
1250 | | |
1251 | 0 | field->native_inline = field->attributes.Lookup("native_inline") != nullptr; |
1252 | 0 | if (field->native_inline && !IsStruct(field->value.type) && |
1253 | 0 | !IsVectorOfStruct(field->value.type) && |
1254 | 0 | !IsVectorOfTable(field->value.type)) |
1255 | 0 | return Error( |
1256 | 0 | "'native_inline' can only be defined on structs, vector of structs or " |
1257 | 0 | "vector of tables"); |
1258 | | |
1259 | 0 | auto nested = field->attributes.Lookup("nested_flatbuffer"); |
1260 | 0 | if (nested) { |
1261 | 0 | if (nested->type.base_type != BASE_TYPE_STRING) |
1262 | 0 | return Error( |
1263 | 0 | "nested_flatbuffer attribute must be a string (the root type)"); |
1264 | 0 | if (!IsVector(type.base_type) || type.element != BASE_TYPE_UCHAR) |
1265 | 0 | return Error( |
1266 | 0 | "nested_flatbuffer attribute may only apply to a vector of ubyte"); |
1267 | | // This will cause an error if the root type of the nested flatbuffer |
1268 | | // wasn't defined elsewhere. |
1269 | 0 | field->nested_flatbuffer = LookupCreateStruct(nested->constant); |
1270 | 0 | } |
1271 | | |
1272 | 0 | if (field->attributes.Lookup("flexbuffer")) { |
1273 | 0 | field->flexbuffer = true; |
1274 | 0 | uses_flexbuffers_ = true; |
1275 | 0 | if (type.base_type != BASE_TYPE_VECTOR || type.element != BASE_TYPE_UCHAR) |
1276 | 0 | return Error("flexbuffer attribute may only apply to a vector of ubyte"); |
1277 | 0 | } |
1278 | | |
1279 | 0 | if (typefield) { |
1280 | 0 | if (!IsScalar(typefield->value.type.base_type)) { |
1281 | | // this is a union vector field |
1282 | 0 | typefield->presence = field->presence; |
1283 | 0 | } |
1284 | | // If this field is a union, and it has a manually assigned id, |
1285 | | // the automatically added type field should have an id as well (of N - 1). |
1286 | 0 | auto attr = field->attributes.Lookup("id"); |
1287 | 0 | if (attr) { |
1288 | 0 | const auto& id_str = attr->constant; |
1289 | 0 | voffset_t id = 0; |
1290 | 0 | const auto done = !atot(id_str.c_str(), *this, &id).Check(); |
1291 | 0 | if (done && id > 0) { |
1292 | 0 | auto val = new Value(); |
1293 | 0 | val->type = attr->type; |
1294 | 0 | val->constant = NumToString(id - 1); |
1295 | 0 | typefield->attributes.Add("id", val); |
1296 | 0 | } else { |
1297 | 0 | return Error( |
1298 | 0 | "a union type effectively adds two fields with non-negative ids, " |
1299 | 0 | "its id must be that of the second field (the first field is " |
1300 | 0 | "the type field and not explicitly declared in the schema);\n" |
1301 | 0 | "field: " + |
1302 | 0 | field->name + ", id: " + id_str); |
1303 | 0 | } |
1304 | 0 | } |
1305 | | // if this field is a union that is deprecated, |
1306 | | // the automatically added type field should be deprecated as well |
1307 | 0 | if (field->deprecated) { |
1308 | 0 | typefield->deprecated = true; |
1309 | 0 | } |
1310 | 0 | } |
1311 | | |
1312 | 0 | EXPECT(';'); |
1313 | 0 | return NoError(); |
1314 | 0 | } |
1315 | | |
1316 | 142k | CheckedError Parser::ParseString(Value& val, bool use_string_pooling) { |
1317 | 142k | auto s = attribute_; |
1318 | 142k | EXPECT(kTokenStringConstant); |
1319 | 142k | if (use_string_pooling) { |
1320 | 0 | val.constant = NumToString(builder_.CreateSharedString(s).o); |
1321 | 142k | } else { |
1322 | 142k | val.constant = NumToString(builder_.CreateString(s).o); |
1323 | 142k | } |
1324 | 142k | return NoError(); |
1325 | 142k | } |
1326 | | |
1327 | 2.94M | CheckedError Parser::ParseComma() { |
1328 | 2.94M | if (!opts.protobuf_ascii_alike) EXPECT(','); |
1329 | 2.94M | return NoError(); |
1330 | 2.94M | } |
1331 | | |
1332 | | CheckedError Parser::ParseAnyValue(Value& val, FieldDef* field, |
1333 | | size_t parent_fieldn, |
1334 | | const StructDef* parent_struct_def, |
1335 | 842k | size_t count, bool inside_vector) { |
1336 | 842k | switch (val.type.base_type) { |
1337 | 4.36k | case BASE_TYPE_UNION: { |
1338 | 4.36k | FLATBUFFERS_ASSERT(field); |
1339 | 4.36k | std::string constant; |
1340 | 4.36k | Vector<uint8_t>* vector_of_union_types = nullptr; |
1341 | | // Find corresponding type field we may have already parsed. |
1342 | 4.36k | for (auto elem = field_stack_.rbegin() + count; |
1343 | 4.86k | elem != field_stack_.rbegin() + parent_fieldn + count; ++elem) { |
1344 | 2.48k | auto& type = elem->second->value.type; |
1345 | 2.48k | if (type.enum_def == val.type.enum_def) { |
1346 | 2.07k | if (inside_vector) { |
1347 | 0 | if (IsVector(type) && type.element == BASE_TYPE_UTYPE) { |
1348 | | // Vector of union type field. |
1349 | 0 | uoffset_t offset; |
1350 | 0 | ECHECK(atot(elem->first.constant.c_str(), *this, &offset)); |
1351 | 0 | vector_of_union_types = reinterpret_cast<Vector<uint8_t>*>( |
1352 | 0 | builder_.GetCurrentBufferPointer() + builder_.GetSize() - |
1353 | 0 | offset); |
1354 | 0 | break; |
1355 | 0 | } |
1356 | 2.07k | } else { |
1357 | 2.07k | if (type.base_type == BASE_TYPE_UTYPE) { |
1358 | | // Union type field. |
1359 | 1.98k | constant = elem->first.constant; |
1360 | 1.98k | break; |
1361 | 1.98k | } |
1362 | 2.07k | } |
1363 | 2.07k | } |
1364 | 2.48k | } |
1365 | 4.36k | if (constant.empty() && !inside_vector) { |
1366 | | // We haven't seen the type field yet. Sadly a lot of JSON writers |
1367 | | // output these in alphabetical order, meaning it comes after this |
1368 | | // value. So we scan past the value to find it, then come back here. |
1369 | | // We currently don't do this for vectors of unions because the |
1370 | | // scanning/serialization logic would get very complicated. |
1371 | 2.38k | auto type_name = field->name + UnionTypeFieldSuffix(); |
1372 | 2.38k | FLATBUFFERS_ASSERT(parent_struct_def); |
1373 | 2.38k | auto type_field = parent_struct_def->fields.Lookup(type_name); |
1374 | 2.38k | FLATBUFFERS_ASSERT(type_field); // Guaranteed by ParseField(). |
1375 | | // Remember where we are in the source file, so we can come back here. |
1376 | 2.38k | auto backup = *static_cast<ParserState*>(this); |
1377 | 2.38k | ECHECK(SkipAnyJsonValue()); // The table. |
1378 | 2.32k | ECHECK(ParseComma()); |
1379 | 2.31k | auto next_name = attribute_; |
1380 | 2.31k | if (Is(kTokenStringConstant)) { |
1381 | 43 | NEXT(); |
1382 | 2.27k | } else { |
1383 | 2.27k | EXPECT(kTokenIdentifier); |
1384 | 2.26k | } |
1385 | 2.30k | if (next_name == type_name) { |
1386 | 2.26k | EXPECT(':'); |
1387 | 2.26k | ParseDepthGuard depth_guard(this); |
1388 | 2.26k | ECHECK(depth_guard.Check()); |
1389 | 2.26k | Value type_val = type_field->value; |
1390 | 2.26k | ECHECK(ParseAnyValue(type_val, type_field, 0, nullptr, 0)); |
1391 | 2.26k | constant = type_val.constant; |
1392 | | // Got the information we needed, now rewind: |
1393 | 2.26k | *static_cast<ParserState*>(this) = backup; |
1394 | 2.26k | } |
1395 | 2.30k | } |
1396 | 4.28k | if (constant.empty() && !vector_of_union_types) { |
1397 | 42 | return Error("missing type field for this union value: " + field->name); |
1398 | 42 | } |
1399 | 4.24k | uint8_t enum_idx; |
1400 | 4.24k | if (vector_of_union_types) { |
1401 | 0 | if (vector_of_union_types->size() <= count) |
1402 | 0 | return Error( |
1403 | 0 | "union types vector smaller than union values vector for: " + |
1404 | 0 | field->name); |
1405 | 0 | enum_idx = vector_of_union_types->Get(static_cast<uoffset_t>(count)); |
1406 | 4.24k | } else { |
1407 | 4.24k | ECHECK(atot(constant.c_str(), *this, &enum_idx)); |
1408 | 4.22k | } |
1409 | 4.22k | auto enum_val = val.type.enum_def->ReverseLookup(enum_idx, true); |
1410 | 4.22k | if (!enum_val) return Error("illegal type id for: " + field->name); |
1411 | 4.20k | if (enum_val->union_type.base_type == BASE_TYPE_STRUCT) { |
1412 | 4.20k | ECHECK(ParseTable(*enum_val->union_type.struct_def, &val.constant, |
1413 | 4.20k | nullptr)); |
1414 | 3.99k | if (enum_val->union_type.struct_def->fixed) { |
1415 | | // All BASE_TYPE_UNION values are offsets, so turn this into one. |
1416 | 0 | SerializeStruct(*enum_val->union_type.struct_def, val); |
1417 | 0 | builder_.ClearOffsets(); |
1418 | 0 | val.constant = NumToString(builder_.GetSize()); |
1419 | 0 | } |
1420 | 3.99k | } else if (IsString(enum_val->union_type)) { |
1421 | 0 | ECHECK(ParseString(val, field->shared)); |
1422 | 0 | } else { |
1423 | 0 | FLATBUFFERS_ASSERT(false); |
1424 | 0 | } |
1425 | 3.99k | break; |
1426 | 4.20k | } |
1427 | 233k | case BASE_TYPE_STRUCT: |
1428 | 233k | ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr)); |
1429 | 230k | break; |
1430 | 142k | case BASE_TYPE_STRING: { |
1431 | 142k | ECHECK(ParseString(val, field->shared)); |
1432 | 142k | break; |
1433 | 142k | } |
1434 | 0 | case BASE_TYPE_VECTOR64: |
1435 | 14.6k | case BASE_TYPE_VECTOR: { |
1436 | 14.6k | uoffset_t off; |
1437 | 14.6k | ECHECK(ParseVector(val.type, &off, field, parent_fieldn)); |
1438 | 13.1k | val.constant = NumToString(off); |
1439 | 13.1k | break; |
1440 | 14.6k | } |
1441 | 0 | case BASE_TYPE_ARRAY: { |
1442 | 0 | ECHECK(ParseArray(val)); |
1443 | 0 | break; |
1444 | 0 | } |
1445 | 2.35k | case BASE_TYPE_INT: |
1446 | 47.8k | case BASE_TYPE_UINT: |
1447 | 159k | case BASE_TYPE_LONG: |
1448 | 213k | case BASE_TYPE_ULONG: { |
1449 | 213k | if (field && field->attributes.Lookup("hash") && |
1450 | 59.8k | (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) { |
1451 | 5.75k | ECHECK(ParseHash(val, field)); |
1452 | 208k | } else { |
1453 | 208k | ECHECK(ParseSingleValue(field ? &field->name : nullptr, val, false)); |
1454 | 208k | } |
1455 | 213k | break; |
1456 | 213k | } |
1457 | 234k | default: |
1458 | 234k | ECHECK(ParseSingleValue(field ? &field->name : nullptr, val, false)); |
1459 | 233k | break; |
1460 | 842k | } |
1461 | 837k | return NoError(); |
1462 | 842k | } |
1463 | | |
1464 | 28.4k | void Parser::SerializeStruct(const StructDef& struct_def, const Value& val) { |
1465 | 28.4k | SerializeStruct(builder_, struct_def, val); |
1466 | 28.4k | } |
1467 | | |
1468 | | void Parser::SerializeStruct(FlatBufferBuilder& builder, |
1469 | 28.4k | const StructDef& struct_def, const Value& val) { |
1470 | 28.4k | FLATBUFFERS_ASSERT(val.constant.length() == struct_def.bytesize); |
1471 | 28.4k | builder.Align(struct_def.minalign); |
1472 | 28.4k | builder.PushBytes(reinterpret_cast<const uint8_t*>(val.constant.c_str()), |
1473 | 28.4k | struct_def.bytesize); |
1474 | 28.4k | builder.AddStructOffset(val.offset, builder.GetSize()); |
1475 | 28.4k | } |
1476 | | |
1477 | | template <typename F> |
1478 | | CheckedError Parser::ParseTableDelimiters(size_t& fieldn, |
1479 | 437k | const StructDef* struct_def, F body) { |
1480 | | // We allow tables both as JSON object{ .. } with field names |
1481 | | // or vector[..] with all fields in order |
1482 | 437k | char terminator = '}'; |
1483 | 437k | bool is_nested_vector = struct_def && Is('['); |
1484 | 437k | if (is_nested_vector) { |
1485 | 31.6k | NEXT(); |
1486 | 31.6k | terminator = ']'; |
1487 | 405k | } else { |
1488 | 405k | EXPECT('{'); |
1489 | 405k | } |
1490 | 1.01M | for (;;) { |
1491 | 1.01M | if ((!opts.strict_json || !fieldn) && Is(terminator)) break; |
1492 | 861k | std::string name; |
1493 | 861k | if (is_nested_vector) { |
1494 | 57.9k | if (fieldn >= struct_def->fields.vec.size()) { |
1495 | 2 | return Error("too many unnamed fields in nested array"); |
1496 | 2 | } |
1497 | 57.9k | name = struct_def->fields.vec[fieldn]->name; |
1498 | 803k | } else { |
1499 | 803k | name = attribute_; |
1500 | 803k | if (Is(kTokenStringConstant)) { |
1501 | 24.9k | NEXT(); |
1502 | 778k | } else { |
1503 | 778k | EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier); |
1504 | 778k | } |
1505 | 803k | if (!opts.protobuf_ascii_alike || !(Is('{') || Is('['))) EXPECT(':'); |
1506 | 803k | } |
1507 | 861k | ECHECK(body(name, fieldn, struct_def)); |
1508 | 849k | if (Is(terminator)) break; |
1509 | 579k | ECHECK(ParseComma()); |
1510 | 576k | } |
1511 | 422k | NEXT(); |
1512 | 422k | if (is_nested_vector && fieldn != struct_def->fields.vec.size()) { |
1513 | 9 | return Error("wrong number of unnamed fields in table vector"); |
1514 | 9 | } |
1515 | 422k | return NoError(); |
1516 | 422k | } idl_parser.cpp:flatbuffers::CheckedError flatbuffers::Parser::ParseTableDelimiters<flatbuffers::Parser::ParseTable(flatbuffers::StructDef const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, unsigned int*)::$_0>(unsigned long&, flatbuffers::StructDef const*, flatbuffers::Parser::ParseTable(flatbuffers::StructDef const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, unsigned int*)::$_0) Line | Count | Source | 1479 | 269k | const StructDef* struct_def, F body) { | 1480 | | // We allow tables both as JSON object{ .. } with field names | 1481 | | // or vector[..] with all fields in order | 1482 | 269k | char terminator = '}'; | 1483 | 269k | bool is_nested_vector = struct_def && Is('['); | 1484 | 269k | if (is_nested_vector) { | 1485 | 31.6k | NEXT(); | 1486 | 31.6k | terminator = ']'; | 1487 | 238k | } else { | 1488 | 238k | EXPECT('{'); | 1489 | 237k | } | 1490 | 433k | for (;;) { | 1491 | 433k | if ((!opts.strict_json || !fieldn) && Is(terminator)) break; | 1492 | 342k | std::string name; | 1493 | 342k | if (is_nested_vector) { | 1494 | 57.9k | if (fieldn >= struct_def->fields.vec.size()) { | 1495 | 2 | return Error("too many unnamed fields in nested array"); | 1496 | 2 | } | 1497 | 57.9k | name = struct_def->fields.vec[fieldn]->name; | 1498 | 284k | } else { | 1499 | 284k | name = attribute_; | 1500 | 284k | if (Is(kTokenStringConstant)) { | 1501 | 2.58k | NEXT(); | 1502 | 281k | } else { | 1503 | 281k | EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier); | 1504 | 281k | } | 1505 | 284k | if (!opts.protobuf_ascii_alike || !(Is('{') || Is('['))) EXPECT(':'); | 1506 | 283k | } | 1507 | 341k | ECHECK(body(name, fieldn, struct_def)); | 1508 | 331k | if (Is(terminator)) break; | 1509 | 167k | ECHECK(ParseComma()); | 1510 | 164k | } | 1511 | 255k | NEXT(); | 1512 | 255k | if (is_nested_vector && fieldn != struct_def->fields.vec.size()) { | 1513 | 9 | return Error("wrong number of unnamed fields in table vector"); | 1514 | 9 | } | 1515 | 255k | return NoError(); | 1516 | 255k | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::Parser::ParseTableDelimiters<flatbuffers::Parser::SkipAnyJsonValue()::$_0>(unsigned long&, flatbuffers::StructDef const*, flatbuffers::Parser::SkipAnyJsonValue()::$_0) Line | Count | Source | 1479 | 92.9k | const StructDef* struct_def, F body) { | 1480 | | // We allow tables both as JSON object{ .. } with field names | 1481 | | // or vector[..] with all fields in order | 1482 | 92.9k | char terminator = '}'; | 1483 | 92.9k | bool is_nested_vector = struct_def && Is('['); | 1484 | 92.9k | if (is_nested_vector) { | 1485 | 0 | NEXT(); | 1486 | 0 | terminator = ']'; | 1487 | 92.9k | } else { | 1488 | 92.9k | EXPECT('{'); | 1489 | 92.9k | } | 1490 | 238k | for (;;) { | 1491 | 238k | if ((!opts.strict_json || !fieldn) && Is(terminator)) break; | 1492 | 233k | std::string name; | 1493 | 233k | if (is_nested_vector) { | 1494 | 0 | if (fieldn >= struct_def->fields.vec.size()) { | 1495 | 0 | return Error("too many unnamed fields in nested array"); | 1496 | 0 | } | 1497 | 0 | name = struct_def->fields.vec[fieldn]->name; | 1498 | 233k | } else { | 1499 | 233k | name = attribute_; | 1500 | 233k | if (Is(kTokenStringConstant)) { | 1501 | 3.50k | NEXT(); | 1502 | 229k | } else { | 1503 | 229k | EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier); | 1504 | 229k | } | 1505 | 232k | if (!opts.protobuf_ascii_alike || !(Is('{') || Is('['))) EXPECT(':'); | 1506 | 232k | } | 1507 | 232k | ECHECK(body(name, fieldn, struct_def)); | 1508 | 232k | if (Is(terminator)) break; | 1509 | 145k | ECHECK(ParseComma()); | 1510 | 145k | } | 1511 | 92.7k | NEXT(); | 1512 | 92.7k | if (is_nested_vector && fieldn != struct_def->fields.vec.size()) { | 1513 | 0 | return Error("wrong number of unnamed fields in table vector"); | 1514 | 0 | } | 1515 | 92.7k | return NoError(); | 1516 | 92.7k | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::Parser::ParseTableDelimiters<flatbuffers::Parser::ParseFlexBufferValue(flexbuffers::Builder*)::$_0>(unsigned long&, flatbuffers::StructDef const*, flatbuffers::Parser::ParseFlexBufferValue(flexbuffers::Builder*)::$_0) Line | Count | Source | 1479 | 74.4k | const StructDef* struct_def, F body) { | 1480 | | // We allow tables both as JSON object{ .. } with field names | 1481 | | // or vector[..] with all fields in order | 1482 | 74.4k | char terminator = '}'; | 1483 | 74.4k | bool is_nested_vector = struct_def && Is('['); | 1484 | 74.4k | if (is_nested_vector) { | 1485 | 0 | NEXT(); | 1486 | 0 | terminator = ']'; | 1487 | 74.4k | } else { | 1488 | 74.4k | EXPECT('{'); | 1489 | 74.4k | } | 1490 | 341k | for (;;) { | 1491 | 341k | if ((!opts.strict_json || !fieldn) && Is(terminator)) break; | 1492 | 286k | std::string name; | 1493 | 286k | if (is_nested_vector) { | 1494 | 0 | if (fieldn >= struct_def->fields.vec.size()) { | 1495 | 0 | return Error("too many unnamed fields in nested array"); | 1496 | 0 | } | 1497 | 0 | name = struct_def->fields.vec[fieldn]->name; | 1498 | 286k | } else { | 1499 | 286k | name = attribute_; | 1500 | 286k | if (Is(kTokenStringConstant)) { | 1501 | 18.9k | NEXT(); | 1502 | 267k | } else { | 1503 | 267k | EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier); | 1504 | 267k | } | 1505 | 286k | if (!opts.protobuf_ascii_alike || !(Is('{') || Is('['))) EXPECT(':'); | 1506 | 286k | } | 1507 | 286k | ECHECK(body(name, fieldn, struct_def)); | 1508 | 285k | if (Is(terminator)) break; | 1509 | 267k | ECHECK(ParseComma()); | 1510 | 267k | } | 1511 | 73.6k | NEXT(); | 1512 | 73.6k | if (is_nested_vector && fieldn != struct_def->fields.vec.size()) { | 1513 | 0 | return Error("wrong number of unnamed fields in table vector"); | 1514 | 0 | } | 1515 | 73.6k | return NoError(); | 1516 | 73.6k | } |
|
1517 | | |
1518 | | CheckedError Parser::ParseTable(const StructDef& struct_def, std::string* value, |
1519 | 269k | uoffset_t* ovalue) { |
1520 | 269k | ParseDepthGuard depth_guard(this); |
1521 | 269k | ECHECK(depth_guard.Check()); |
1522 | | |
1523 | 269k | size_t fieldn_outer = 0; |
1524 | 269k | auto err = ParseTableDelimiters( |
1525 | 269k | fieldn_outer, &struct_def, |
1526 | 269k | [&](const std::string& name, size_t& fieldn, |
1527 | 341k | const StructDef* struct_def_inner) -> CheckedError { |
1528 | 341k | if (name == "$schema") { |
1529 | 212 | ECHECK(Expect(kTokenStringConstant)); |
1530 | 207 | return NoError(); |
1531 | 212 | } |
1532 | 341k | auto field = struct_def_inner->fields.Lookup(name); |
1533 | 341k | if (!field) { |
1534 | 32.1k | if (!opts.skip_unexpected_fields_in_json) { |
1535 | 39 | return Error("unknown field: " + name); |
1536 | 32.1k | } else { |
1537 | 32.1k | ECHECK(SkipAnyJsonValue()); |
1538 | 31.7k | } |
1539 | 309k | } else { |
1540 | 309k | if (IsIdent("null") && !IsScalar(field->value.type.base_type)) { |
1541 | 198 | ECHECK(Next()); // Ignore this field. |
1542 | 309k | } else { |
1543 | 309k | Value val = field->value; |
1544 | 309k | if (field->flexbuffer) { |
1545 | 18.8k | flexbuffers::Builder builder(1024, |
1546 | 18.8k | flexbuffers::BUILDER_FLAG_SHARE_ALL); |
1547 | 18.8k | ECHECK(ParseFlexBufferValue(&builder)); |
1548 | 14.1k | builder.Finish(); |
1549 | | // Force alignment for nested flexbuffer |
1550 | 14.1k | builder_.ForceVectorAlignment(builder.GetSize(), sizeof(uint8_t), |
1551 | 14.1k | sizeof(largest_scalar_t)); |
1552 | 14.1k | auto off = builder_.CreateVector(builder.GetBuffer()); |
1553 | 14.1k | val.constant = NumToString(off.o); |
1554 | 290k | } else if (field->nested_flatbuffer) { |
1555 | 12.7k | ECHECK( |
1556 | 12.7k | ParseNestedFlatbuffer(val, field, fieldn, struct_def_inner)); |
1557 | 277k | } else { |
1558 | 277k | ECHECK(ParseAnyValue(val, field, fieldn, struct_def_inner, 0)); |
1559 | 273k | } |
1560 | | // Hardcoded insertion-sort with error-check. |
1561 | | // If fields are specified in order, then this loop exits |
1562 | | // immediately. |
1563 | 299k | auto elem = field_stack_.rbegin(); |
1564 | 351k | for (; elem != field_stack_.rbegin() + fieldn; ++elem) { |
1565 | 142k | auto existing_field = elem->second; |
1566 | 142k | if (existing_field == field) |
1567 | 134 | return Error("field set more than once: " + field->name); |
1568 | 142k | if (existing_field->value.offset < field->value.offset) break; |
1569 | 142k | } |
1570 | | // Note: elem points to before the insertion point, thus .base() |
1571 | | // points to the correct spot. |
1572 | 299k | field_stack_.insert(elem.base(), std::make_pair(val, field)); |
1573 | 299k | fieldn++; |
1574 | 299k | } |
1575 | 309k | } |
1576 | 331k | return NoError(); |
1577 | 341k | }); |
1578 | 269k | ECHECK(err); |
1579 | | |
1580 | | // Check if all required fields are parsed. |
1581 | 255k | for (auto field_it = struct_def.fields.vec.begin(); |
1582 | 5.41M | field_it != struct_def.fields.vec.end(); ++field_it) { |
1583 | 5.16M | auto required_field = *field_it; |
1584 | 5.16M | if (!required_field->IsRequired()) { |
1585 | 5.08M | continue; |
1586 | 5.08M | } |
1587 | 77.6k | bool found = false; |
1588 | 77.6k | for (auto pf_it = field_stack_.end() - fieldn_outer; |
1589 | 84.1k | pf_it != field_stack_.end(); ++pf_it) { |
1590 | 83.9k | auto parsed_field = pf_it->second; |
1591 | 83.9k | if (parsed_field == required_field) { |
1592 | 77.5k | found = true; |
1593 | 77.5k | break; |
1594 | 77.5k | } |
1595 | 83.9k | } |
1596 | 77.6k | if (!found) { |
1597 | 189 | return Error("required field is missing: " + required_field->name + |
1598 | 189 | " in " + struct_def.name); |
1599 | 189 | } |
1600 | 77.6k | } |
1601 | | |
1602 | 255k | if (struct_def.fixed && fieldn_outer != struct_def.fields.vec.size()) |
1603 | 3 | return Error("struct: wrong number of initializers: " + struct_def.name); |
1604 | | |
1605 | 255k | auto start = struct_def.fixed ? builder_.StartStruct(struct_def.minalign) |
1606 | 255k | : builder_.StartTable(); |
1607 | | |
1608 | 1.18M | for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; size; |
1609 | 928k | size /= 2) { |
1610 | | // Go through elements in reverse, since we're building the data backwards. |
1611 | | // TODO(derekbailey): this doesn't work when there are Offset64 fields, as |
1612 | | // those have to be built first. So this needs to be changed to iterate over |
1613 | | // Offset64 then Offset32 fields. |
1614 | 928k | for (auto it = field_stack_.rbegin(); |
1615 | 1.87M | it != field_stack_.rbegin() + fieldn_outer; ++it) { |
1616 | 944k | auto& field_value = it->first; |
1617 | 944k | auto field = it->second; |
1618 | 944k | if (!struct_def.sortbysize || |
1619 | 868k | size == SizeOf(field_value.type.base_type)) { |
1620 | 293k | switch (field_value.type.base_type) { |
1621 | | // clang-format off |
1622 | 0 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ |
1623 | 162k | case BASE_TYPE_ ## ENUM: \ |
1624 | 161k | builder_.Pad(field->padding); \ |
1625 | 162k | if (struct_def.fixed) { \ |
1626 | 72.1k | CTYPE val; \ |
1627 | 72.1k | ECHECK(atot(field_value.constant.c_str(), *this, &val)); \ |
1628 | 71.7k | builder_.PushElement(val); \ |
1629 | 90.1k | } else { \ |
1630 | 90.1k | if (field->IsScalarOptional()) { \ |
1631 | 0 | if (field_value.constant != "null") { \ |
1632 | 0 | CTYPE val; \ |
1633 | 0 | ECHECK(atot(field_value.constant.c_str(), *this, &val)); \ |
1634 | 0 | builder_.AddElement(field_value.offset, val); \ |
1635 | 0 | } \ |
1636 | 90.1k | } else { \ |
1637 | 90.1k | CTYPE val, valdef; \ |
1638 | 90.1k | ECHECK(atot(field_value.constant.c_str(), *this, &val)); \ |
1639 | 89.5k | ECHECK(atot(field->value.constant.c_str(), *this, &valdef)); \ |
1640 | 89.5k | builder_.AddElement(field_value.offset, val, valdef); \ |
1641 | 89.5k | } \ |
1642 | 90.1k | } \ |
1643 | 161k | break; |
1644 | 162k | FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD) |
1645 | 5.58k | #undef FLATBUFFERS_TD |
1646 | 5.58k | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ |
1647 | 130k | case BASE_TYPE_ ## ENUM: \ |
1648 | 130k | builder_.Pad(field->padding); \ |
1649 | 130k | if (IsStruct(field->value.type)) { \ |
1650 | 7.30k | SerializeStruct(*field->value.type.struct_def, field_value); \ |
1651 | 123k | } else { \ |
1652 | | /* Special case for fields that use 64-bit addressing */ \ |
1653 | 123k | if(field->offset64) { \ |
1654 | 0 | Offset64<void> offset; \ |
1655 | 0 | ECHECK(atot(field_value.constant.c_str(), *this, &offset)); \ |
1656 | 0 | builder_.AddOffset(field_value.offset, offset); \ |
1657 | 123k | } else { \ |
1658 | 123k | CTYPE val; \ |
1659 | 123k | ECHECK(atot(field_value.constant.c_str(), *this, &val)); \ |
1660 | 123k | builder_.AddOffset(field_value.offset, val); \ |
1661 | 123k | } \ |
1662 | 123k | } \ |
1663 | 130k | break; |
1664 | 130k | FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD) |
1665 | 3.59k | #undef FLATBUFFERS_TD |
1666 | 3.59k | case BASE_TYPE_ARRAY: |
1667 | 0 | builder_.Pad(field->padding); |
1668 | 0 | builder_.PushBytes( |
1669 | 0 | reinterpret_cast<const uint8_t*>(field_value.constant.c_str()), |
1670 | 0 | InlineSize(field_value.type)); |
1671 | 0 | break; |
1672 | | // clang-format on |
1673 | 293k | } |
1674 | 293k | } |
1675 | 944k | } |
1676 | 928k | } |
1677 | 546k | for (size_t i = 0; i < fieldn_outer; i++) field_stack_.pop_back(); |
1678 | | |
1679 | 254k | if (struct_def.fixed) { |
1680 | 30.5k | builder_.ClearOffsets(); |
1681 | 30.5k | builder_.EndStruct(); |
1682 | 30.5k | FLATBUFFERS_ASSERT(value); |
1683 | | // Temporarily store this struct in the value string, since it is to |
1684 | | // be serialized in-place elsewhere. |
1685 | 30.5k | value->assign( |
1686 | 30.5k | reinterpret_cast<const char*>(builder_.GetCurrentBufferPointer()), |
1687 | 30.5k | struct_def.bytesize); |
1688 | 30.5k | builder_.PopBytes(struct_def.bytesize); |
1689 | 30.5k | FLATBUFFERS_ASSERT(!ovalue); |
1690 | 224k | } else { |
1691 | 224k | auto val = builder_.EndTable(start); |
1692 | 224k | if (ovalue) *ovalue = val; |
1693 | 224k | if (value) *value = NumToString(val); |
1694 | 224k | } |
1695 | 254k | return NoError(); |
1696 | 254k | } |
1697 | | |
1698 | | template <typename F> |
1699 | 1.07M | CheckedError Parser::ParseVectorDelimiters(size_t& count, F body) { |
1700 | 1.07M | EXPECT('['); |
1701 | 3.43M | for (;;) { |
1702 | 3.43M | if ((!opts.strict_json || !count) && Is(']')) break; |
1703 | 3.40M | ECHECK(body(count)); |
1704 | 3.40M | count++; |
1705 | 3.40M | if (Is(']')) break; |
1706 | 2.36M | ECHECK(ParseComma()); |
1707 | 2.36M | } |
1708 | 1.06M | NEXT(); |
1709 | 1.06M | return NoError(); |
1710 | 1.06M | } idl_parser.cpp:flatbuffers::CheckedError flatbuffers::Parser::ParseVectorDelimiters<flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_0>(unsigned long&, flatbuffers::Parser::ParseVector(flatbuffers::Type const&, unsigned int*, flatbuffers::FieldDef*, unsigned long)::$_0) Line | Count | Source | 1699 | 14.6k | CheckedError Parser::ParseVectorDelimiters(size_t& count, F body) { | 1700 | 14.6k | EXPECT('['); | 1701 | 567k | for (;;) { | 1702 | 567k | if ((!opts.strict_json || !count) && Is(']')) break; | 1703 | 562k | ECHECK(body(count)); | 1704 | 561k | count++; | 1705 | 561k | if (Is(']')) break; | 1706 | 552k | ECHECK(ParseComma()); | 1707 | 552k | } | 1708 | 13.3k | NEXT(); | 1709 | 13.3k | return NoError(); | 1710 | 13.3k | } |
Unexecuted instantiation: idl_parser.cpp:flatbuffers::CheckedError flatbuffers::Parser::ParseVectorDelimiters<flatbuffers::Parser::ParseArray(flatbuffers::Value&)::$_0>(unsigned long&, flatbuffers::Parser::ParseArray(flatbuffers::Value&)::$_0) idl_parser.cpp:flatbuffers::CheckedError flatbuffers::Parser::ParseVectorDelimiters<flatbuffers::Parser::SkipAnyJsonValue()::$_1>(unsigned long&, flatbuffers::Parser::SkipAnyJsonValue()::$_1) Line | Count | Source | 1699 | 370k | CheckedError Parser::ParseVectorDelimiters(size_t& count, F body) { | 1700 | 370k | EXPECT('['); | 1701 | 945k | for (;;) { | 1702 | 945k | if ((!opts.strict_json || !count) && Is(']')) break; | 1703 | 937k | ECHECK(body(count)); | 1704 | 937k | count++; | 1705 | 937k | if (Is(']')) break; | 1706 | 575k | ECHECK(ParseComma()); | 1707 | 575k | } | 1708 | 369k | NEXT(); | 1709 | 369k | return NoError(); | 1710 | 369k | } |
idl_parser.cpp:flatbuffers::CheckedError flatbuffers::Parser::ParseVectorDelimiters<flatbuffers::Parser::ParseFlexBufferValue(flexbuffers::Builder*)::$_1>(unsigned long&, flatbuffers::Parser::ParseFlexBufferValue(flexbuffers::Builder*)::$_1) Line | Count | Source | 1699 | 685k | CheckedError Parser::ParseVectorDelimiters(size_t& count, F body) { | 1700 | 685k | EXPECT('['); | 1701 | 1.91M | for (;;) { | 1702 | 1.91M | if ((!opts.strict_json || !count) && Is(']')) break; | 1703 | 1.90M | ECHECK(body(count)); | 1704 | 1.90M | count++; | 1705 | 1.90M | if (Is(']')) break; | 1706 | 1.23M | ECHECK(ParseComma()); | 1707 | 1.23M | } | 1708 | 684k | NEXT(); | 1709 | 684k | return NoError(); | 1710 | 684k | } |
|
1711 | | |
1712 | | CheckedError Parser::ParseAlignAttribute(const std::string& align_constant, |
1713 | 0 | size_t min_align, size_t* align) { |
1714 | | // Use uint8_t to avoid problems with size_t==`unsigned long` on LP64. |
1715 | 0 | uint8_t align_value; |
1716 | 0 | if (StringToNumber(align_constant.c_str(), &align_value) && |
1717 | 0 | VerifyAlignmentRequirements(static_cast<size_t>(align_value), |
1718 | 0 | min_align)) { |
1719 | 0 | *align = align_value; |
1720 | 0 | return NoError(); |
1721 | 0 | } |
1722 | 0 | return Error("unexpected force_align value '" + align_constant + |
1723 | 0 | "', alignment must be a power of two integer ranging from the " |
1724 | 0 | "type\'s natural alignment " + |
1725 | 0 | NumToString(min_align) + " to " + |
1726 | 0 | NumToString(FLATBUFFERS_MAX_ALIGNMENT)); |
1727 | 0 | } |
1728 | | |
1729 | | CheckedError Parser::ParseVector(const Type& vector_type, uoffset_t* ovalue, |
1730 | 14.6k | FieldDef* field, size_t fieldn) { |
1731 | 14.6k | Type type = vector_type.VectorType(); |
1732 | 14.6k | size_t count = 0; |
1733 | 562k | auto err = ParseVectorDelimiters(count, [&](size_t&) -> CheckedError { |
1734 | 562k | Value val; |
1735 | 562k | val.type = type; |
1736 | 562k | ECHECK(ParseAnyValue(val, field, fieldn, nullptr, count, true)); |
1737 | 561k | field_stack_.push_back(std::make_pair(val, nullptr)); |
1738 | 561k | return NoError(); |
1739 | 562k | }); |
1740 | 14.6k | ECHECK(err); |
1741 | | |
1742 | 13.3k | const size_t alignment = InlineAlignment(type); |
1743 | 13.3k | const size_t len = count * InlineSize(type) / InlineAlignment(type); |
1744 | 13.3k | const size_t elemsize = InlineAlignment(type); |
1745 | 13.3k | const auto force_align = field->attributes.Lookup("force_align"); |
1746 | 13.3k | if (force_align) { |
1747 | 0 | size_t align; |
1748 | 0 | ECHECK(ParseAlignAttribute(force_align->constant, 1, &align)); |
1749 | 0 | if (align > 1) { |
1750 | 0 | builder_.ForceVectorAlignment(len, elemsize, align); |
1751 | 0 | } |
1752 | 0 | } |
1753 | | |
1754 | | // TODO Fix using element alignment as size (`elemsize`)! |
1755 | 13.3k | if (vector_type.base_type == BASE_TYPE_VECTOR64) { |
1756 | | // TODO(derekbailey): this requires a 64-bit builder. |
1757 | | // builder_.StartVector<Offset64, uoffset64_t>(len, elemsize, alignment); |
1758 | 0 | builder_.StartVector(len, elemsize, alignment); |
1759 | 13.3k | } else { |
1760 | 13.3k | builder_.StartVector(len, elemsize, alignment); |
1761 | 13.3k | } |
1762 | 549k | for (size_t i = 0; i < count; i++) { |
1763 | | // start at the back, since we're building the data backwards. |
1764 | 536k | auto& val = field_stack_.back().first; |
1765 | 536k | switch (val.type.base_type) { |
1766 | | // clang-format off |
1767 | 0 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE,...) \ |
1768 | 536k | case BASE_TYPE_ ## ENUM: \ |
1769 | 536k | if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \ |
1770 | 536k | else { \ |
1771 | 515k | CTYPE elem; \ |
1772 | 515k | ECHECK(atot(val.constant.c_str(), *this, &elem)); \ |
1773 | 515k | builder_.PushElement(elem); \ |
1774 | 515k | } \ |
1775 | 536k | break; |
1776 | 536k | FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) |
1777 | 536k | #undef FLATBUFFERS_TD |
1778 | | // clang-format on |
1779 | 536k | } |
1780 | 536k | field_stack_.pop_back(); |
1781 | 536k | } |
1782 | | |
1783 | 13.1k | builder_.ClearOffsets(); |
1784 | 13.1k | if (vector_type.base_type == BASE_TYPE_VECTOR64) { |
1785 | 0 | *ovalue = builder_.EndVector<uoffset64_t>(count); |
1786 | 13.1k | } else { |
1787 | 13.1k | *ovalue = builder_.EndVector(count); |
1788 | 13.1k | } |
1789 | | |
1790 | 13.1k | if (type.base_type == BASE_TYPE_STRUCT && type.struct_def->has_key) { |
1791 | | // We should sort this vector. Find the key first. |
1792 | 4.46k | const FieldDef* key = nullptr; |
1793 | 4.46k | for (auto it = type.struct_def->fields.vec.begin(); |
1794 | 13.8k | it != type.struct_def->fields.vec.end(); ++it) { |
1795 | 13.8k | if ((*it)->key) { |
1796 | 4.46k | key = (*it); |
1797 | 4.46k | break; |
1798 | 4.46k | } |
1799 | 13.8k | } |
1800 | 4.46k | FLATBUFFERS_ASSERT(key); |
1801 | | // Now sort it. |
1802 | | // We can't use std::sort because for structs the size is not known at |
1803 | | // compile time, and for tables our iterators dereference offsets, so can't |
1804 | | // be used to swap elements. |
1805 | | // And we can't use C qsort either, since that would force use to use |
1806 | | // globals, making parsing thread-unsafe. |
1807 | | // So for now, we use SimpleQsort above. |
1808 | | // TODO: replace with something better, preferably not recursive. |
1809 | | |
1810 | 4.46k | if (type.struct_def->fixed) { |
1811 | 354 | const voffset_t offset = key->value.offset; |
1812 | 354 | const size_t struct_size = type.struct_def->bytesize; |
1813 | 354 | auto v = |
1814 | 354 | reinterpret_cast<VectorOfAny*>(builder_.GetCurrentBufferPointer()); |
1815 | 354 | SimpleQsort<uint8_t>( |
1816 | 354 | v->Data(), v->Data() + v->size() * type.struct_def->bytesize, |
1817 | 354 | type.struct_def->bytesize, |
1818 | 10.5M | [offset, key](const uint8_t* a, const uint8_t* b) -> bool { |
1819 | 10.5M | return CompareSerializedScalars(a + offset, b + offset, *key); |
1820 | 10.5M | }, |
1821 | 24.5k | [struct_size](uint8_t* a, uint8_t* b) { |
1822 | | // FIXME: faster? |
1823 | 220k | for (size_t i = 0; i < struct_size; i++) { |
1824 | 196k | std::swap(a[i], b[i]); |
1825 | 196k | } |
1826 | 24.5k | }); |
1827 | 4.11k | } else { |
1828 | 4.11k | auto v = reinterpret_cast<Vector<Offset<Table>>*>( |
1829 | 4.11k | builder_.GetCurrentBufferPointer()); |
1830 | | // Here also can't use std::sort. We do have an iterator type for it, |
1831 | | // but it is non-standard as it will dereference the offsets, and thus |
1832 | | // can't be used to swap elements. |
1833 | 4.11k | if (key->value.type.base_type == BASE_TYPE_STRING) { |
1834 | 2.69k | SimpleQsort<Offset<Table>>( |
1835 | 2.69k | v->data(), v->data() + v->size(), 1, |
1836 | 2.06M | [key](const Offset<Table>* _a, const Offset<Table>* _b) -> bool { |
1837 | 2.06M | return CompareTablesByStringKey(_a, _b, *key); |
1838 | 2.06M | }, |
1839 | 2.69k | SwapSerializedTables); |
1840 | 2.69k | } else { |
1841 | 1.42k | SimpleQsort<Offset<Table>>( |
1842 | 1.42k | v->data(), v->data() + v->size(), 1, |
1843 | 153M | [key](const Offset<Table>* _a, const Offset<Table>* _b) -> bool { |
1844 | 153M | return CompareTablesByScalarKey(_a, _b, *key); |
1845 | 153M | }, |
1846 | 1.42k | SwapSerializedTables); |
1847 | 1.42k | } |
1848 | 4.11k | } |
1849 | 4.46k | } |
1850 | 13.1k | return NoError(); |
1851 | 13.1k | } |
1852 | | |
1853 | 0 | CheckedError Parser::ParseArray(Value& array) { |
1854 | 0 | std::vector<Value> stack; |
1855 | 0 | FlatBufferBuilder builder; |
1856 | 0 | const auto& type = array.type.VectorType(); |
1857 | 0 | auto length = array.type.fixed_length; |
1858 | 0 | size_t count = 0; |
1859 | 0 | auto err = ParseVectorDelimiters(count, [&](size_t&) -> CheckedError { |
1860 | 0 | stack.emplace_back(Value()); |
1861 | 0 | auto& val = stack.back(); |
1862 | 0 | val.type = type; |
1863 | 0 | if (IsStruct(type)) { |
1864 | 0 | ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr)); |
1865 | 0 | } else { |
1866 | 0 | ECHECK(ParseSingleValue(nullptr, val, false)); |
1867 | 0 | } |
1868 | 0 | return NoError(); |
1869 | 0 | }); |
1870 | 0 | ECHECK(err); |
1871 | 0 | if (length != count) return Error("Fixed-length array size is incorrect."); |
1872 | | |
1873 | 0 | for (auto it = stack.rbegin(); it != stack.rend(); ++it) { |
1874 | 0 | auto& val = *it; |
1875 | | // clang-format off |
1876 | 0 | switch (val.type.base_type) { |
1877 | 0 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ |
1878 | 0 | case BASE_TYPE_ ## ENUM: \ |
1879 | 0 | if (IsStruct(val.type)) { \ |
1880 | 0 | SerializeStruct(builder, *val.type.struct_def, val); \ |
1881 | 0 | } else { \ |
1882 | 0 | CTYPE elem; \ |
1883 | 0 | ECHECK(atot(val.constant.c_str(), *this, &elem)); \ |
1884 | 0 | builder.PushElement(elem); \ |
1885 | 0 | } \ |
1886 | 0 | break; |
1887 | 0 | FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) |
1888 | 0 | #undef FLATBUFFERS_TD |
1889 | 0 | default: FLATBUFFERS_ASSERT(0); |
1890 | 0 | } |
1891 | | // clang-format on |
1892 | 0 | } |
1893 | | |
1894 | 0 | array.constant.assign( |
1895 | 0 | reinterpret_cast<const char*>(builder.GetCurrentBufferPointer()), |
1896 | 0 | InlineSize(array.type)); |
1897 | 0 | return NoError(); |
1898 | 0 | } |
1899 | | |
1900 | | CheckedError Parser::ParseNestedFlatbuffer(Value& val, FieldDef* field, |
1901 | | size_t fieldn, |
1902 | 12.7k | const StructDef* parent_struct_def) { |
1903 | 12.7k | if (token_ == '[') { // backwards compat for 'legacy' ubyte buffers |
1904 | 7 | if (opts.json_nested_legacy_flatbuffers) { |
1905 | 0 | ECHECK(ParseAnyValue(val, field, fieldn, parent_struct_def, 0)); |
1906 | 7 | } else { |
1907 | 7 | return Error( |
1908 | 7 | "cannot parse nested_flatbuffer as bytes unless" |
1909 | 7 | " --json-nested-bytes is set"); |
1910 | 7 | } |
1911 | 12.7k | } else { |
1912 | 12.7k | auto cursor_at_value_begin = cursor_; |
1913 | 12.7k | ECHECK(SkipAnyJsonValue()); |
1914 | 12.7k | std::string substring(cursor_at_value_begin - 1, cursor_ - 1); |
1915 | | |
1916 | | // Create and initialize new parser |
1917 | 12.7k | Parser nested_parser; |
1918 | 12.7k | FLATBUFFERS_ASSERT(field->nested_flatbuffer); |
1919 | 12.7k | nested_parser.root_struct_def_ = field->nested_flatbuffer; |
1920 | 12.7k | nested_parser.enums_ = enums_; |
1921 | 12.7k | nested_parser.opts = opts; |
1922 | 12.7k | nested_parser.uses_flexbuffers_ = uses_flexbuffers_; |
1923 | 12.7k | nested_parser.parse_depth_counter_ = parse_depth_counter_; |
1924 | | // Parse JSON substring into new flatbuffer builder using nested_parser |
1925 | 12.7k | bool ok = nested_parser.Parse(substring.c_str(), nullptr, nullptr); |
1926 | | |
1927 | | // Clean nested_parser to avoid deleting the elements in |
1928 | | // the SymbolTables on destruction |
1929 | 12.7k | nested_parser.enums_.dict.clear(); |
1930 | 12.7k | nested_parser.enums_.vec.clear(); |
1931 | | |
1932 | 12.7k | if (!ok) { |
1933 | 1.00k | ECHECK(Error(nested_parser.error_)); |
1934 | 0 | } |
1935 | | // Force alignment for nested flatbuffer |
1936 | 11.7k | builder_.ForceVectorAlignment( |
1937 | 11.7k | nested_parser.builder_.GetSize(), sizeof(uint8_t), |
1938 | 11.7k | nested_parser.builder_.GetBufferMinAlignment()); |
1939 | | |
1940 | 11.7k | auto off = builder_.CreateVector(nested_parser.builder_.GetBufferPointer(), |
1941 | 11.7k | nested_parser.builder_.GetSize()); |
1942 | 11.7k | val.constant = NumToString(off.o); |
1943 | 11.7k | } |
1944 | 11.7k | return NoError(); |
1945 | 12.7k | } |
1946 | | |
1947 | 0 | CheckedError Parser::ParseMetaData(SymbolTable<Value>* attributes) { |
1948 | 0 | if (Is('(')) { |
1949 | 0 | NEXT(); |
1950 | 0 | for (;;) { |
1951 | 0 | auto name = attribute_; |
1952 | 0 | if (false == (Is(kTokenIdentifier) || Is(kTokenStringConstant))) |
1953 | 0 | return Error("attribute name must be either identifier or string: " + |
1954 | 0 | name); |
1955 | 0 | if (known_attributes_.find(name) == known_attributes_.end()) |
1956 | 0 | return Error("user define attributes must be declared before use: " + |
1957 | 0 | name); |
1958 | 0 | NEXT(); |
1959 | 0 | auto e = new Value(); |
1960 | 0 | if (attributes->Add(name, e)) Warning("attribute already found: " + name); |
1961 | 0 | if (Is(':')) { |
1962 | 0 | NEXT(); |
1963 | 0 | ECHECK(ParseSingleValue(&name, *e, true)); |
1964 | 0 | } |
1965 | 0 | if (Is(')')) { |
1966 | 0 | NEXT(); |
1967 | 0 | break; |
1968 | 0 | } |
1969 | 0 | EXPECT(','); |
1970 | 0 | } |
1971 | 0 | } |
1972 | 0 | return NoError(); |
1973 | 0 | } |
1974 | | |
1975 | | CheckedError Parser::ParseEnumFromString(const Type& type, |
1976 | 26.1k | std::string* result) { |
1977 | 26.1k | const auto base_type = |
1978 | 26.1k | type.enum_def ? type.enum_def->underlying_type.base_type : type.base_type; |
1979 | 26.1k | if (!IsInteger(base_type)) return Error("not a valid value for this field"); |
1980 | 26.1k | uint64_t u64 = 0; |
1981 | 64.6k | for (size_t pos = 0; pos != std::string::npos;) { |
1982 | 38.8k | const auto delim = attribute_.find_first_of(' ', pos); |
1983 | 38.8k | const auto last = (std::string::npos == delim); |
1984 | 38.8k | auto word = attribute_.substr(pos, !last ? delim - pos : std::string::npos); |
1985 | 38.8k | pos = !last ? delim + 1 : std::string::npos; |
1986 | 38.8k | const EnumVal* ev = nullptr; |
1987 | 38.8k | if (type.enum_def) { |
1988 | 34.0k | ev = type.enum_def->Lookup(word); |
1989 | 34.0k | } else { |
1990 | 4.81k | auto dot = word.find_first_of('.'); |
1991 | 4.81k | if (std::string::npos == dot) |
1992 | 50 | return Error("enum values need to be qualified by an enum type"); |
1993 | 4.76k | auto enum_def_str = word.substr(0, dot); |
1994 | 4.76k | const auto enum_def = LookupEnum(enum_def_str); |
1995 | 4.76k | if (!enum_def) return Error("unknown enum: " + enum_def_str); |
1996 | 4.52k | auto enum_val_str = word.substr(dot + 1); |
1997 | 4.52k | ev = enum_def->Lookup(enum_val_str); |
1998 | 4.52k | } |
1999 | 38.6k | if (!ev) return Error("unknown enum value: " + word); |
2000 | 38.4k | u64 |= ev->GetAsUInt64(); |
2001 | 38.4k | } |
2002 | 25.7k | *result = IsUnsigned(base_type) ? NumToString(u64) |
2003 | 25.7k | : NumToString(static_cast<int64_t>(u64)); |
2004 | 25.7k | return NoError(); |
2005 | 26.1k | } |
2006 | | |
2007 | 5.75k | CheckedError Parser::ParseHash(Value& e, FieldDef* field) { |
2008 | 5.75k | FLATBUFFERS_ASSERT(field); |
2009 | 5.75k | Value* hash_name = field->attributes.Lookup("hash"); |
2010 | 5.75k | switch (e.type.base_type) { |
2011 | 0 | case BASE_TYPE_SHORT: { |
2012 | 0 | auto hash = FindHashFunction16(hash_name->constant.c_str()); |
2013 | 0 | int16_t hashed_value = static_cast<int16_t>(hash(attribute_.c_str())); |
2014 | 0 | e.constant = NumToString(hashed_value); |
2015 | 0 | break; |
2016 | 0 | } |
2017 | 0 | case BASE_TYPE_USHORT: { |
2018 | 0 | auto hash = FindHashFunction16(hash_name->constant.c_str()); |
2019 | 0 | uint16_t hashed_value = hash(attribute_.c_str()); |
2020 | 0 | e.constant = NumToString(hashed_value); |
2021 | 0 | break; |
2022 | 0 | } |
2023 | 598 | case BASE_TYPE_INT: { |
2024 | 598 | auto hash = FindHashFunction32(hash_name->constant.c_str()); |
2025 | 598 | int32_t hashed_value = static_cast<int32_t>(hash(attribute_.c_str())); |
2026 | 598 | e.constant = NumToString(hashed_value); |
2027 | 598 | break; |
2028 | 0 | } |
2029 | 1.53k | case BASE_TYPE_UINT: { |
2030 | 1.53k | auto hash = FindHashFunction32(hash_name->constant.c_str()); |
2031 | 1.53k | uint32_t hashed_value = hash(attribute_.c_str()); |
2032 | 1.53k | e.constant = NumToString(hashed_value); |
2033 | 1.53k | break; |
2034 | 0 | } |
2035 | 1.89k | case BASE_TYPE_LONG: { |
2036 | 1.89k | auto hash = FindHashFunction64(hash_name->constant.c_str()); |
2037 | 1.89k | int64_t hashed_value = static_cast<int64_t>(hash(attribute_.c_str())); |
2038 | 1.89k | e.constant = NumToString(hashed_value); |
2039 | 1.89k | break; |
2040 | 0 | } |
2041 | 1.73k | case BASE_TYPE_ULONG: { |
2042 | 1.73k | auto hash = FindHashFunction64(hash_name->constant.c_str()); |
2043 | 1.73k | uint64_t hashed_value = hash(attribute_.c_str()); |
2044 | 1.73k | e.constant = NumToString(hashed_value); |
2045 | 1.73k | break; |
2046 | 0 | } |
2047 | 0 | default: |
2048 | 0 | FLATBUFFERS_ASSERT(0); |
2049 | 5.75k | } |
2050 | 5.75k | NEXT(); |
2051 | 5.75k | return NoError(); |
2052 | 5.75k | } |
2053 | | |
2054 | 595 | CheckedError Parser::TokenError() { |
2055 | 595 | return Error("cannot parse value starting with: " + TokenToStringId(token_)); |
2056 | 595 | } |
2057 | | |
2058 | 3.76k | CheckedError Parser::ParseFunction(const std::string* name, Value& e) { |
2059 | 3.76k | ParseDepthGuard depth_guard(this); |
2060 | 3.76k | ECHECK(depth_guard.Check()); |
2061 | | |
2062 | | // Copy name, attribute will be changed on NEXT(). |
2063 | 3.76k | const auto functionname = attribute_; |
2064 | 3.76k | if (!IsFloat(e.type.base_type)) { |
2065 | 26 | return Error(functionname + ": type of argument mismatch, expecting: " + |
2066 | 26 | TypeName(BASE_TYPE_DOUBLE) + |
2067 | 26 | ", found: " + TypeName(e.type.base_type) + |
2068 | 26 | ", name: " + (name ? *name : "") + ", value: " + e.constant); |
2069 | 26 | } |
2070 | 3.73k | NEXT(); |
2071 | 3.73k | EXPECT('('); |
2072 | 3.72k | ECHECK(ParseSingleValue(name, e, false)); |
2073 | 3.34k | EXPECT(')'); |
2074 | | // calculate with double precision |
2075 | 3.30k | double x, y = 0.0; |
2076 | 3.30k | ECHECK(atot(e.constant.c_str(), *this, &x)); |
2077 | | // clang-format off |
2078 | 3.28k | auto func_match = false; |
2079 | 3.28k | #define FLATBUFFERS_FN_DOUBLE(name, op) \ |
2080 | 26.2k | if (!func_match && functionname == name) { y = op; func_match = true; } |
2081 | 3.28k | FLATBUFFERS_FN_DOUBLE("deg", x / kPi * 180); |
2082 | 3.28k | FLATBUFFERS_FN_DOUBLE("rad", x * kPi / 180); |
2083 | 3.28k | FLATBUFFERS_FN_DOUBLE("sin", sin(x)); |
2084 | 3.28k | FLATBUFFERS_FN_DOUBLE("cos", cos(x)); |
2085 | 3.28k | FLATBUFFERS_FN_DOUBLE("tan", tan(x)); |
2086 | 3.28k | FLATBUFFERS_FN_DOUBLE("asin", asin(x)); |
2087 | 3.28k | FLATBUFFERS_FN_DOUBLE("acos", acos(x)); |
2088 | 3.28k | FLATBUFFERS_FN_DOUBLE("atan", atan(x)); |
2089 | | // TODO(wvo): add more useful conversion functions here. |
2090 | 3.28k | #undef FLATBUFFERS_FN_DOUBLE |
2091 | | // clang-format on |
2092 | 3.28k | if (true != func_match) { |
2093 | 47 | return Error(std::string("Unknown conversion function: ") + functionname + |
2094 | 47 | ", field name: " + (name ? *name : "") + |
2095 | 47 | ", value: " + e.constant); |
2096 | 47 | } |
2097 | 3.23k | e.constant = NumToString(y); |
2098 | 3.23k | return NoError(); |
2099 | 3.28k | } |
2100 | | |
2101 | | CheckedError Parser::TryTypedValue(const std::string* name, int dtoken, |
2102 | | bool check, Value& e, BaseType req, |
2103 | 415k | bool* destmatch) { |
2104 | 415k | FLATBUFFERS_ASSERT(*destmatch == false && dtoken == token_); |
2105 | 415k | *destmatch = true; |
2106 | 415k | e.constant = attribute_; |
2107 | | // Check token match |
2108 | 415k | if (!check) { |
2109 | 0 | if (e.type.base_type == BASE_TYPE_NONE) { |
2110 | 0 | e.type.base_type = req; |
2111 | 0 | } else { |
2112 | 0 | return Error(std::string("type mismatch: expecting: ") + |
2113 | 0 | TypeName(e.type.base_type) + ", found: " + TypeName(req) + |
2114 | 0 | ", name: " + (name ? *name : "") + ", value: " + e.constant); |
2115 | 0 | } |
2116 | 0 | } |
2117 | | // The exponent suffix of hexadecimal float-point number is mandatory. |
2118 | | // A hex-integer constant is forbidden as an initializer of float number. |
2119 | 415k | if ((kTokenFloatConstant != dtoken) && IsFloat(e.type.base_type)) { |
2120 | 40.4k | const auto& s = e.constant; |
2121 | 40.4k | const auto k = s.find_first_of("0123456789."); |
2122 | 40.4k | if ((std::string::npos != k) && (s.length() > (k + 1)) && |
2123 | 3.42k | (s[k] == '0' && is_alpha_char(s[k + 1], 'X')) && |
2124 | 472 | (std::string::npos == s.find_first_of("pP", k + 2))) { |
2125 | 33 | return Error( |
2126 | 33 | "invalid number, the exponent suffix of hexadecimal " |
2127 | 33 | "floating-point literals is mandatory: \"" + |
2128 | 33 | s + "\""); |
2129 | 33 | } |
2130 | 40.4k | } |
2131 | 415k | NEXT(); |
2132 | 415k | return NoError(); |
2133 | 415k | } |
2134 | | |
2135 | | CheckedError Parser::ParseSingleValue(const std::string* name, Value& e, |
2136 | 445k | bool check_now) { |
2137 | 445k | if (token_ == '+' || token_ == '-') { |
2138 | 1.30k | const char sign = static_cast<char>(token_); |
2139 | | // Get an indentifier: NAN, INF, or function name like cos/sin/deg. |
2140 | 1.30k | NEXT(); |
2141 | 1.30k | if (token_ != kTokenIdentifier) return Error("constant name expected"); |
2142 | 1.30k | attribute_.insert(size_t(0), size_t(1), sign); |
2143 | 1.30k | } |
2144 | | |
2145 | 445k | const auto in_type = e.type.base_type; |
2146 | 445k | const auto is_tok_ident = (token_ == kTokenIdentifier); |
2147 | 445k | const auto is_tok_string = (token_ == kTokenStringConstant); |
2148 | | |
2149 | | // First see if this could be a conversion function. |
2150 | 445k | if (is_tok_ident && *cursor_ == '(') { |
2151 | 3.76k | return ParseFunction(name, e); |
2152 | 3.76k | } |
2153 | | |
2154 | | // clang-format off |
2155 | 442k | auto match = false; |
2156 | | |
2157 | 442k | #define IF_ECHECK_(force, dtoken, check, req) \ |
2158 | 971k | if (!match && ((dtoken) == token_) && ((check) || flatbuffers::IsConstTrue(force))) \ |
2159 | 971k | ECHECK(TryTypedValue(name, dtoken, check, e, req, &match)) |
2160 | 529k | #define TRY_ECHECK(dtoken, check, req) IF_ECHECK_(false, dtoken, check, req) |
2161 | 442k | #define FORCE_ECHECK(dtoken, check, req) IF_ECHECK_(true, dtoken, check, req) |
2162 | | // clang-format on |
2163 | | |
2164 | 442k | if (is_tok_ident || is_tok_string) { |
2165 | 40.4k | const auto kTokenStringOrIdent = token_; |
2166 | | // The string type is a most probable type, check it first. |
2167 | 40.4k | TRY_ECHECK(kTokenStringConstant, in_type == BASE_TYPE_STRING, |
2168 | 40.4k | BASE_TYPE_STRING); |
2169 | | |
2170 | | // avoid escaped and non-ascii in the string |
2171 | 40.4k | if (!match && is_tok_string && IsScalar(in_type) && |
2172 | 27.1k | !attr_is_trivial_ascii_string_) { |
2173 | 5 | return Error( |
2174 | 5 | std::string("type mismatch or invalid value, an initializer of " |
2175 | 5 | "non-string field must be trivial ASCII string: type: ") + |
2176 | 5 | TypeName(in_type) + ", name: " + (name ? *name : "") + |
2177 | 5 | ", value: " + attribute_); |
2178 | 5 | } |
2179 | | |
2180 | | // A boolean as true/false. Boolean as Integer check below. |
2181 | 40.4k | if (!match && IsBool(in_type)) { |
2182 | 9.13k | auto is_true = attribute_ == "true"; |
2183 | 9.13k | if (is_true || attribute_ == "false") { |
2184 | 7.54k | attribute_ = is_true ? "1" : "0"; |
2185 | | // accepts both kTokenStringConstant and kTokenIdentifier |
2186 | 7.54k | TRY_ECHECK(kTokenStringOrIdent, IsBool(in_type), BASE_TYPE_BOOL); |
2187 | 7.54k | } |
2188 | 9.13k | } |
2189 | | // Check for optional scalars. |
2190 | 40.4k | if (!match && IsScalar(in_type) && attribute_ == "null") { |
2191 | 400 | e.constant = "null"; |
2192 | 400 | NEXT(); |
2193 | 399 | match = true; |
2194 | 399 | } |
2195 | | // Check if this could be a string/identifier enum value. |
2196 | | // Enum can have only true integer base type. |
2197 | 40.4k | if (!match && IsInteger(in_type) && !IsBool(in_type) && |
2198 | 26.8k | IsIdentifierStart(*attribute_.c_str())) { |
2199 | 26.1k | ECHECK(ParseEnumFromString(e.type, &e.constant)); |
2200 | 25.7k | NEXT(); |
2201 | 25.7k | match = true; |
2202 | 25.7k | } |
2203 | | // Parse a float/integer number from the string. |
2204 | | // A "scalar-in-string" value needs extra checks. |
2205 | 39.9k | if (!match && is_tok_string && IsScalar(in_type)) { |
2206 | | // Strip trailing whitespaces from attribute_. |
2207 | 1.10k | auto last_non_ws = attribute_.find_last_not_of(' '); |
2208 | 1.10k | if (std::string::npos != last_non_ws) attribute_.resize(last_non_ws + 1); |
2209 | 1.10k | if (IsFloat(e.type.base_type)) { |
2210 | | // The functions strtod() and strtof() accept both 'nan' and |
2211 | | // 'nan(number)' literals. While 'nan(number)' is rejected by the parser |
2212 | | // as an unsupported function if is_tok_ident is true. |
2213 | 734 | if (attribute_.find_last_of(')') != std::string::npos) { |
2214 | 21 | return Error("invalid number: " + attribute_); |
2215 | 21 | } |
2216 | 734 | } |
2217 | 1.10k | } |
2218 | | // Float numbers or nan, inf, pi, etc. |
2219 | 39.9k | TRY_ECHECK(kTokenStringOrIdent, IsFloat(in_type), BASE_TYPE_FLOAT); |
2220 | | // An integer constant in string. |
2221 | 39.9k | TRY_ECHECK(kTokenStringOrIdent, IsInteger(in_type), BASE_TYPE_INT); |
2222 | | // Unknown tokens will be interpreted as string type. |
2223 | | // An attribute value may be a scalar or string constant. |
2224 | 39.9k | FORCE_ECHECK(kTokenStringConstant, in_type == BASE_TYPE_STRING, |
2225 | 39.9k | BASE_TYPE_STRING); |
2226 | 401k | } else { |
2227 | | // Try a float number. |
2228 | 401k | TRY_ECHECK(kTokenFloatConstant, IsFloat(in_type), BASE_TYPE_FLOAT); |
2229 | | // Integer token can init any scalar (integer of float). |
2230 | 401k | FORCE_ECHECK(kTokenIntegerConstant, IsScalar(in_type), BASE_TYPE_INT); |
2231 | 401k | } |
2232 | | // Match empty vectors for default-empty-vectors. |
2233 | 441k | if (!match && IsVector(e.type) && token_ == '[') { |
2234 | 0 | NEXT(); |
2235 | 0 | if (token_ != ']') { |
2236 | 0 | return Error("Expected `]` in vector default"); |
2237 | 0 | } |
2238 | 0 | NEXT(); |
2239 | 0 | match = true; |
2240 | 0 | e.constant = "[]"; |
2241 | 0 | } |
2242 | | |
2243 | 441k | #undef FORCE_ECHECK |
2244 | 441k | #undef TRY_ECHECK |
2245 | 441k | #undef IF_ECHECK_ |
2246 | | |
2247 | 441k | if (!match) { |
2248 | 114 | std::string msg; |
2249 | 114 | msg += "Cannot assign token starting with '" + TokenToStringId(token_) + |
2250 | 114 | "' to value of <" + std::string(TypeName(in_type)) + "> type."; |
2251 | 114 | return Error(msg); |
2252 | 114 | } |
2253 | 441k | const auto match_type = e.type.base_type; // may differ from in_type |
2254 | | // The check_now flag must be true when parse a fbs-schema. |
2255 | | // This flag forces to check default scalar values or metadata of field. |
2256 | | // For JSON parser the flag should be false. |
2257 | | // If it is set for JSON each value will be checked twice (see ParseTable). |
2258 | | // Special case 'null' since atot can't handle that. |
2259 | 441k | if (check_now && IsScalar(match_type) && e.constant != "null") { |
2260 | | // clang-format off |
2261 | 0 | switch (match_type) { |
2262 | 0 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ |
2263 | 0 | case BASE_TYPE_ ## ENUM: {\ |
2264 | 0 | CTYPE val; \ |
2265 | 0 | ECHECK(atot(e.constant.c_str(), *this, &val)); \ |
2266 | 0 | SingleValueRepack(e, val); \ |
2267 | 0 | break; } |
2268 | 0 | FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD) |
2269 | 0 | #undef FLATBUFFERS_TD |
2270 | 0 | default: break; |
2271 | 0 | } |
2272 | | // clang-format on |
2273 | 0 | } |
2274 | 441k | return NoError(); |
2275 | 441k | } |
2276 | | |
2277 | | StructDef* Parser::LookupCreateStruct(const std::string& name, |
2278 | 0 | bool create_if_new, bool definition) { |
2279 | 0 | std::string qualified_name = current_namespace_->GetFullyQualifiedName(name); |
2280 | | // See if it exists pre-declared by an unqualified use. |
2281 | 0 | auto struct_def = LookupStruct(name); |
2282 | 0 | if (struct_def && struct_def->predecl) { |
2283 | 0 | if (definition) { |
2284 | | // Make sure it has the current namespace, and is registered under its |
2285 | | // qualified name. |
2286 | 0 | struct_def->defined_namespace = current_namespace_; |
2287 | 0 | structs_.Move(name, qualified_name); |
2288 | 0 | } |
2289 | 0 | return struct_def; |
2290 | 0 | } |
2291 | | // See if it exists pre-declared by an qualified use. |
2292 | 0 | struct_def = LookupStruct(qualified_name); |
2293 | 0 | if (struct_def && struct_def->predecl) { |
2294 | 0 | if (definition) { |
2295 | | // Make sure it has the current namespace. |
2296 | 0 | struct_def->defined_namespace = current_namespace_; |
2297 | 0 | } |
2298 | 0 | return struct_def; |
2299 | 0 | } |
2300 | 0 | if (!definition && !struct_def) { |
2301 | 0 | struct_def = LookupStructThruParentNamespaces(name); |
2302 | 0 | } |
2303 | 0 | if (!struct_def && create_if_new) { |
2304 | 0 | struct_def = new StructDef(); |
2305 | 0 | if (definition) { |
2306 | 0 | structs_.Add(qualified_name, struct_def); |
2307 | 0 | struct_def->name = name; |
2308 | 0 | struct_def->defined_namespace = current_namespace_; |
2309 | 0 | } else { |
2310 | | // Not a definition. |
2311 | | // Rather than failing, we create a "pre declared" StructDef, due to |
2312 | | // circular references, and check for errors at the end of parsing. |
2313 | | // It is defined in the current namespace, as the best guess what the |
2314 | | // final namespace will be. |
2315 | 0 | structs_.Add(name, struct_def); |
2316 | 0 | struct_def->name = name; |
2317 | 0 | struct_def->defined_namespace = current_namespace_; |
2318 | 0 | struct_def->original_location.reset( |
2319 | 0 | new std::string(file_being_parsed_ + ":" + NumToString(line_))); |
2320 | 0 | } |
2321 | 0 | } |
2322 | 0 | return struct_def; |
2323 | 0 | } |
2324 | | |
2325 | 0 | const EnumVal* EnumDef::MinValue() const { |
2326 | 0 | return vals.vec.empty() ? nullptr : vals.vec.front(); |
2327 | 0 | } |
2328 | 0 | const EnumVal* EnumDef::MaxValue() const { |
2329 | 0 | return vals.vec.empty() ? nullptr : vals.vec.back(); |
2330 | 0 | } |
2331 | | |
2332 | 0 | uint64_t EnumDef::Distance(const EnumVal* v1, const EnumVal* v2) const { |
2333 | 0 | return IsUInt64() ? EnumDistanceImpl(v1->GetAsUInt64(), v2->GetAsUInt64()) |
2334 | 0 | : EnumDistanceImpl(v1->GetAsInt64(), v2->GetAsInt64()); |
2335 | 0 | } |
2336 | | |
2337 | 0 | std::string EnumDef::AllFlags() const { |
2338 | 0 | FLATBUFFERS_ASSERT(attributes.Lookup("bit_flags")); |
2339 | 0 | uint64_t u64 = 0; |
2340 | 0 | for (auto it = Vals().begin(); it != Vals().end(); ++it) { |
2341 | 0 | u64 |= (*it)->GetAsUInt64(); |
2342 | 0 | } |
2343 | 0 | return IsUInt64() ? NumToString(u64) : NumToString(static_cast<int64_t>(u64)); |
2344 | 0 | } |
2345 | | |
2346 | | EnumVal* EnumDef::ReverseLookup(int64_t enum_idx, |
2347 | 91.7k | bool skip_union_default) const { |
2348 | 91.7k | auto skip_first = static_cast<int>(is_union && skip_union_default); |
2349 | 304k | for (auto it = Vals().begin() + skip_first; it != Vals().end(); ++it) { |
2350 | 254k | if ((*it)->GetAsInt64() == enum_idx) { |
2351 | 41.4k | return *it; |
2352 | 41.4k | } |
2353 | 254k | } |
2354 | 50.2k | return nullptr; |
2355 | 91.7k | } |
2356 | | |
2357 | 0 | EnumVal* EnumDef::FindByValue(const std::string& constant) const { |
2358 | 0 | int64_t i64; |
2359 | 0 | auto done = false; |
2360 | 0 | if (IsUInt64()) { |
2361 | 0 | uint64_t u64; // avoid reinterpret_cast of pointers |
2362 | 0 | done = StringToNumber(constant.c_str(), &u64); |
2363 | 0 | i64 = static_cast<int64_t>(u64); |
2364 | 0 | } else { |
2365 | 0 | done = StringToNumber(constant.c_str(), &i64); |
2366 | 0 | } |
2367 | 0 | FLATBUFFERS_ASSERT(done); |
2368 | 0 | if (!done) return nullptr; |
2369 | 0 | return ReverseLookup(i64, false); |
2370 | 0 | } |
2371 | | |
2372 | 0 | void EnumDef::SortByValue() { |
2373 | 0 | auto& v = vals.vec; |
2374 | 0 | if (IsUInt64()) |
2375 | 0 | std::sort(v.begin(), v.end(), [](const EnumVal* e1, const EnumVal* e2) { |
2376 | 0 | if (e1->GetAsUInt64() == e2->GetAsUInt64()) { |
2377 | 0 | return e1->name < e2->name; |
2378 | 0 | } |
2379 | 0 | return e1->GetAsUInt64() < e2->GetAsUInt64(); |
2380 | 0 | }); |
2381 | 0 | else |
2382 | 0 | std::sort(v.begin(), v.end(), [](const EnumVal* e1, const EnumVal* e2) { |
2383 | 0 | if (e1->GetAsInt64() == e2->GetAsInt64()) { |
2384 | 0 | return e1->name < e2->name; |
2385 | 0 | } |
2386 | 0 | return e1->GetAsInt64() < e2->GetAsInt64(); |
2387 | 0 | }); |
2388 | 0 | } |
2389 | | |
2390 | 0 | void EnumDef::RemoveDuplicates() { |
2391 | | // This method depends form SymbolTable implementation! |
2392 | | // 1) vals.vec - owner (raw pointer) |
2393 | | // 2) vals.dict - access map |
2394 | 0 | auto first = vals.vec.begin(); |
2395 | 0 | auto last = vals.vec.end(); |
2396 | 0 | if (first == last) return; |
2397 | 0 | auto result = first; |
2398 | 0 | while (++first != last) { |
2399 | 0 | if ((*result)->value != (*first)->value) { |
2400 | 0 | *(++result) = *first; |
2401 | 0 | } else { |
2402 | 0 | auto ev = *first; |
2403 | 0 | for (auto it = vals.dict.begin(); it != vals.dict.end(); ++it) { |
2404 | 0 | if (it->second == ev) it->second = *result; // reassign |
2405 | 0 | } |
2406 | 0 | delete ev; // delete enum value |
2407 | 0 | *first = nullptr; |
2408 | 0 | } |
2409 | 0 | } |
2410 | 0 | vals.vec.erase(++result, last); |
2411 | 0 | } |
2412 | | |
2413 | | template <typename T> |
2414 | 0 | void EnumDef::ChangeEnumValue(EnumVal* ev, T new_value) { |
2415 | 0 | ev->value = static_cast<int64_t>(new_value); |
2416 | 0 | } |
2417 | | |
2418 | | namespace EnumHelper { |
2419 | | template <BaseType E> |
2420 | | struct EnumValType { |
2421 | | typedef int64_t type; |
2422 | | }; |
2423 | | template <> |
2424 | | struct EnumValType<BASE_TYPE_ULONG> { |
2425 | | typedef uint64_t type; |
2426 | | }; |
2427 | | } // namespace EnumHelper |
2428 | | |
2429 | | struct EnumValBuilder { |
2430 | 0 | EnumVal* CreateEnumerator(const std::string& ev_name) { |
2431 | 0 | FLATBUFFERS_ASSERT(!temp); |
2432 | 0 | auto first = enum_def.vals.vec.empty(); |
2433 | 0 | user_value = first; |
2434 | 0 | temp = new EnumVal(ev_name, first ? 0 : enum_def.vals.vec.back()->value); |
2435 | 0 | return temp; |
2436 | 0 | } |
2437 | | |
2438 | 0 | EnumVal* CreateEnumerator(const std::string& ev_name, int64_t val) { |
2439 | 0 | FLATBUFFERS_ASSERT(!temp); |
2440 | 0 | user_value = true; |
2441 | 0 | temp = new EnumVal(ev_name, val); |
2442 | 0 | return temp; |
2443 | 0 | } |
2444 | | |
2445 | 0 | FLATBUFFERS_CHECKED_ERROR AcceptEnumerator(const std::string& name) { |
2446 | 0 | FLATBUFFERS_ASSERT(temp); |
2447 | 0 | ECHECK(ValidateValue(&temp->value, false == user_value)); |
2448 | 0 | FLATBUFFERS_ASSERT((temp->union_type.enum_def == nullptr) || |
2449 | 0 | (temp->union_type.enum_def == &enum_def)); |
2450 | 0 | auto not_unique = enum_def.vals.Add(name, temp); |
2451 | 0 | temp = nullptr; |
2452 | 0 | if (not_unique) return parser.Error("enum value already exists: " + name); |
2453 | 0 | return NoError(); |
2454 | 0 | } |
2455 | | |
2456 | 0 | FLATBUFFERS_CHECKED_ERROR AcceptEnumerator() { |
2457 | 0 | return AcceptEnumerator(temp->name); |
2458 | 0 | } |
2459 | | |
2460 | 0 | FLATBUFFERS_CHECKED_ERROR AssignEnumeratorValue(const std::string& value) { |
2461 | 0 | user_value = true; |
2462 | 0 | auto fit = false; |
2463 | 0 | if (enum_def.IsUInt64()) { |
2464 | 0 | uint64_t u64; |
2465 | 0 | fit = StringToNumber(value.c_str(), &u64); |
2466 | 0 | temp->value = static_cast<int64_t>(u64); // well-defined since C++20. |
2467 | 0 | } else { |
2468 | 0 | int64_t i64; |
2469 | 0 | fit = StringToNumber(value.c_str(), &i64); |
2470 | 0 | temp->value = i64; |
2471 | 0 | } |
2472 | 0 | if (!fit) return parser.Error("enum value does not fit, \"" + value + "\""); |
2473 | 0 | return NoError(); |
2474 | 0 | } |
2475 | | |
2476 | | template <BaseType E, typename CTYPE> |
2477 | 0 | inline FLATBUFFERS_CHECKED_ERROR ValidateImpl(int64_t* ev, int m) { |
2478 | 0 | typedef typename EnumHelper::EnumValType<E>::type T; // int64_t or uint64_t |
2479 | 0 | static_assert(sizeof(T) == sizeof(int64_t), "invalid EnumValType"); |
2480 | 0 | const auto v = static_cast<T>(*ev); |
2481 | 0 | auto up = static_cast<T>((flatbuffers::numeric_limits<CTYPE>::max)()); |
2482 | 0 | auto dn = static_cast<T>((flatbuffers::numeric_limits<CTYPE>::lowest)()); |
2483 | 0 | if (v < dn || v > (up - m)) { |
2484 | 0 | return parser.Error("enum value does not fit, \"" + NumToString(v) + |
2485 | 0 | (m ? " + 1\"" : "\"") + " out of " + |
2486 | 0 | TypeToIntervalString<CTYPE>()); |
2487 | 0 | } |
2488 | 0 | *ev = static_cast<int64_t>(v + m); // well-defined since C++20. |
2489 | 0 | return NoError(); |
2490 | 0 | } Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)0, unsigned char>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)1, unsigned char>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)2, unsigned char>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)3, signed char>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)4, unsigned char>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)5, short>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)6, unsigned short>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)7, int>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)8, unsigned int>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)9, long>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)10, unsigned long>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)11, float>(long*, int) Unexecuted instantiation: flatbuffers::CheckedError flatbuffers::EnumValBuilder::ValidateImpl<(flatbuffers::BaseType)12, double>(long*, int) |
2491 | | |
2492 | 0 | FLATBUFFERS_CHECKED_ERROR ValidateValue(int64_t* ev, bool next) { |
2493 | | // clang-format off |
2494 | 0 | switch (enum_def.underlying_type.base_type) { |
2495 | 0 | #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, ...) \ |
2496 | 0 | case BASE_TYPE_##ENUM: { \ |
2497 | 0 | if (!IsInteger(BASE_TYPE_##ENUM)) break; \ |
2498 | 0 | return ValidateImpl<BASE_TYPE_##ENUM, CTYPE>(ev, next ? 1 : 0); \ |
2499 | 0 | } |
2500 | 0 | FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD) |
2501 | 0 | #undef FLATBUFFERS_TD |
2502 | 0 | default: break; |
2503 | 0 | } |
2504 | | // clang-format on |
2505 | 0 | return parser.Error("fatal: invalid enum underlying type"); |
2506 | 0 | } |
2507 | | |
2508 | | EnumValBuilder(Parser& _parser, EnumDef& _enum_def) |
2509 | 0 | : parser(_parser), |
2510 | 0 | enum_def(_enum_def), |
2511 | 0 | temp(nullptr), |
2512 | 0 | user_value(false) {} |
2513 | | |
2514 | 0 | ~EnumValBuilder() { delete temp; } |
2515 | | |
2516 | | Parser& parser; |
2517 | | EnumDef& enum_def; |
2518 | | EnumVal* temp; |
2519 | | bool user_value; |
2520 | | }; |
2521 | | |
2522 | | CheckedError Parser::ParseEnum(const bool is_union, EnumDef** dest, |
2523 | 1 | const char* filename) { |
2524 | 1 | std::vector<std::string> enum_comment = doc_comment_; |
2525 | 1 | NEXT(); |
2526 | 1 | std::string enum_name = attribute_; |
2527 | 1 | EXPECT(kTokenIdentifier); |
2528 | 0 | EnumDef* enum_def; |
2529 | 0 | ECHECK(StartEnum(enum_name, is_union, &enum_def)); |
2530 | 0 | if (filename != nullptr && !opts.project_root.empty()) { |
2531 | 0 | enum_def->declaration_file = &GetPooledString(FilePath( |
2532 | 0 | opts.project_root, filename, opts.binary_schema_absolute_paths)); |
2533 | 0 | } |
2534 | 0 | enum_def->doc_comment = enum_comment; |
2535 | 0 | if (!opts.proto_mode) { |
2536 | | // Give specialized error message, since this type spec used to |
2537 | | // be optional in the first FlatBuffers release. |
2538 | 0 | bool explicit_underlying_type = false; |
2539 | 0 | if (!Is(':')) { |
2540 | | // Enum is forced to have an explicit underlying type in declaration. |
2541 | 0 | if (!is_union) { |
2542 | 0 | return Error( |
2543 | 0 | "must specify the underlying integer type for this" |
2544 | 0 | " enum (e.g. \': short\', which was the default)."); |
2545 | 0 | } |
2546 | 0 | } else { |
2547 | | // Union underlying type is only supported for cpp |
2548 | 0 | if (is_union && !SupportsUnionUnderlyingType()) { |
2549 | 0 | return Error( |
2550 | 0 | "Underlying type for union is not yet supported in at least one of " |
2551 | 0 | "the specified programming languages."); |
2552 | 0 | } |
2553 | 0 | NEXT(); |
2554 | 0 | explicit_underlying_type = true; |
2555 | 0 | } |
2556 | | |
2557 | 0 | if (explicit_underlying_type) { |
2558 | | // Specify the integer type underlying this enum. |
2559 | 0 | ECHECK(ParseType(enum_def->underlying_type)); |
2560 | 0 | if (!IsInteger(enum_def->underlying_type.base_type) || |
2561 | 0 | IsBool(enum_def->underlying_type.base_type)) { |
2562 | 0 | return Error("underlying " + std::string(is_union ? "union" : "enum") + |
2563 | 0 | "type must be integral"); |
2564 | 0 | } |
2565 | | |
2566 | | // Make this type refer back to the enum it was derived from. |
2567 | 0 | enum_def->underlying_type.enum_def = enum_def; |
2568 | 0 | } |
2569 | 0 | } |
2570 | 0 | ECHECK(ParseMetaData(&enum_def->attributes)); |
2571 | 0 | const auto underlying_type = enum_def->underlying_type.base_type; |
2572 | 0 | if (enum_def->attributes.Lookup("bit_flags") && |
2573 | 0 | !IsUnsigned(underlying_type)) { |
2574 | | // todo: Convert to the Error in the future? |
2575 | 0 | Warning("underlying type of bit_flags enum must be unsigned"); |
2576 | 0 | } |
2577 | 0 | if (enum_def->attributes.Lookup("force_align")) { |
2578 | 0 | return Error("`force_align` is not a valid attribute for Enums. "); |
2579 | 0 | } |
2580 | 0 | EnumValBuilder evb(*this, *enum_def); |
2581 | 0 | EXPECT('{'); |
2582 | | // A lot of code generatos expect that an enum is not-empty. |
2583 | 0 | if ((is_union || Is('}')) && !opts.proto_mode) { |
2584 | 0 | evb.CreateEnumerator("NONE"); |
2585 | 0 | ECHECK(evb.AcceptEnumerator()); |
2586 | 0 | } |
2587 | 0 | std::set<std::pair<BaseType, StructDef*>> union_types; |
2588 | 0 | while (!Is('}')) { |
2589 | 0 | if (opts.proto_mode && attribute_ == "option") { |
2590 | 0 | ECHECK(ParseProtoOption()); |
2591 | 0 | } else { |
2592 | 0 | auto& ev = *evb.CreateEnumerator(attribute_); |
2593 | 0 | auto full_name = ev.name; |
2594 | 0 | ev.doc_comment = doc_comment_; |
2595 | 0 | EXPECT(kTokenIdentifier); |
2596 | 0 | if (is_union) { |
2597 | 0 | ECHECK(ParseNamespacing(&full_name, &ev.name)); |
2598 | 0 | if (opts.union_value_namespacing) { |
2599 | | // Since we can't namespace the actual enum identifiers, turn |
2600 | | // namespace parts into part of the identifier. |
2601 | 0 | ev.name = full_name; |
2602 | 0 | std::replace(ev.name.begin(), ev.name.end(), '.', '_'); |
2603 | 0 | } |
2604 | 0 | if (Is(':')) { |
2605 | 0 | NEXT(); |
2606 | 0 | ECHECK(ParseType(ev.union_type)); |
2607 | 0 | if (ev.union_type.base_type != BASE_TYPE_STRUCT && |
2608 | 0 | ev.union_type.base_type != BASE_TYPE_STRING) |
2609 | 0 | return Error("union value type may only be table/struct/string"); |
2610 | 0 | } else { |
2611 | 0 | ev.union_type = Type(BASE_TYPE_STRUCT, LookupCreateStruct(full_name)); |
2612 | 0 | } |
2613 | 0 | if (!enum_def->uses_multiple_type_instances) { |
2614 | 0 | auto ins = union_types.insert(std::make_pair( |
2615 | 0 | ev.union_type.base_type, ev.union_type.struct_def)); |
2616 | 0 | enum_def->uses_multiple_type_instances = (false == ins.second); |
2617 | 0 | } |
2618 | 0 | } |
2619 | | |
2620 | 0 | if (Is('=')) { |
2621 | 0 | NEXT(); |
2622 | 0 | ECHECK(evb.AssignEnumeratorValue(attribute_)); |
2623 | 0 | EXPECT(kTokenIntegerConstant); |
2624 | 0 | } |
2625 | | |
2626 | 0 | if (opts.proto_mode && Is('[')) { |
2627 | 0 | NEXT(); |
2628 | | // ignore attributes on enums. |
2629 | 0 | while (token_ != ']') NEXT(); |
2630 | 0 | NEXT(); |
2631 | 0 | } else { |
2632 | | // parse attributes in fbs schema |
2633 | 0 | ECHECK(ParseMetaData(&ev.attributes)); |
2634 | 0 | } |
2635 | | |
2636 | 0 | ECHECK(evb.AcceptEnumerator()); |
2637 | 0 | } |
2638 | 0 | if (!Is(opts.proto_mode ? ';' : ',')) break; |
2639 | 0 | NEXT(); |
2640 | 0 | } |
2641 | 0 | EXPECT('}'); |
2642 | | |
2643 | | // At this point, the enum can be empty if input is invalid proto-file. |
2644 | 0 | if (!enum_def->size()) |
2645 | 0 | return Error("incomplete enum declaration, values not found"); |
2646 | | |
2647 | 0 | if (enum_def->attributes.Lookup("bit_flags")) { |
2648 | 0 | const auto base_width = static_cast<uint64_t>(8 * SizeOf(underlying_type)); |
2649 | 0 | for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end(); |
2650 | 0 | ++it) { |
2651 | 0 | auto ev = *it; |
2652 | 0 | const auto u = ev->GetAsUInt64(); |
2653 | | // Stop manipulations with the sign. |
2654 | 0 | if (!IsUnsigned(underlying_type) && u == (base_width - 1)) |
2655 | 0 | return Error("underlying type of bit_flags enum must be unsigned"); |
2656 | 0 | if (u >= base_width) |
2657 | 0 | return Error("bit flag out of range of underlying integral type"); |
2658 | 0 | enum_def->ChangeEnumValue(ev, 1ULL << u); |
2659 | 0 | } |
2660 | 0 | } |
2661 | | |
2662 | 0 | enum_def->SortByValue(); // Must be sorted to use MinValue/MaxValue. |
2663 | | |
2664 | | // Ensure enum value uniqueness. |
2665 | 0 | auto prev_it = enum_def->Vals().begin(); |
2666 | 0 | for (auto it = prev_it + 1; it != enum_def->Vals().end(); ++it) { |
2667 | 0 | auto prev_ev = *prev_it; |
2668 | 0 | auto ev = *it; |
2669 | 0 | if (prev_ev->GetAsUInt64() == ev->GetAsUInt64()) |
2670 | 0 | return Error("all enum values must be unique: " + prev_ev->name + |
2671 | 0 | " and " + ev->name + " are both " + |
2672 | 0 | NumToString(ev->GetAsInt64())); |
2673 | 0 | } |
2674 | | |
2675 | 0 | if (dest) *dest = enum_def; |
2676 | 0 | const auto qualified_name = |
2677 | 0 | current_namespace_->GetFullyQualifiedName(enum_def->name); |
2678 | 0 | if (types_.Add(qualified_name, new Type(BASE_TYPE_UNION, nullptr, enum_def))) |
2679 | 0 | return Error("datatype already exists: " + qualified_name); |
2680 | 0 | return NoError(); |
2681 | 0 | } |
2682 | | |
2683 | 0 | CheckedError Parser::StartStruct(const std::string& name, StructDef** dest) { |
2684 | 0 | auto& struct_def = *LookupCreateStruct(name, true, true); |
2685 | 0 | if (!struct_def.predecl) |
2686 | 0 | return Error("datatype already exists: " + |
2687 | 0 | current_namespace_->GetFullyQualifiedName(name)); |
2688 | 0 | struct_def.predecl = false; |
2689 | 0 | struct_def.name = name; |
2690 | 0 | struct_def.file = file_being_parsed_; |
2691 | | // Move this struct to the back of the vector just in case it was predeclared, |
2692 | | // to preserve declaration order. |
2693 | 0 | *std::remove(structs_.vec.begin(), structs_.vec.end(), &struct_def) = |
2694 | 0 | &struct_def; |
2695 | 0 | *dest = &struct_def; |
2696 | 0 | return NoError(); |
2697 | 0 | } |
2698 | | |
2699 | | CheckedError Parser::CheckClash(std::vector<FieldDef*>& fields, |
2700 | | StructDef* struct_def, const char* suffix, |
2701 | 0 | BaseType basetype) { |
2702 | 0 | auto len = strlen(suffix); |
2703 | 0 | for (auto it = fields.begin(); it != fields.end(); ++it) { |
2704 | 0 | auto& fname = (*it)->name; |
2705 | 0 | if (fname.length() > len && |
2706 | 0 | fname.compare(fname.length() - len, len, suffix) == 0 && |
2707 | 0 | (*it)->value.type.base_type != BASE_TYPE_UTYPE) { |
2708 | 0 | auto field = |
2709 | 0 | struct_def->fields.Lookup(fname.substr(0, fname.length() - len)); |
2710 | 0 | if (field && field->value.type.base_type == basetype) |
2711 | 0 | return Error("Field " + fname + |
2712 | 0 | " would clash with generated functions for field " + |
2713 | 0 | field->name); |
2714 | 0 | } |
2715 | 0 | } |
2716 | 0 | return NoError(); |
2717 | 0 | } |
2718 | | |
2719 | 0 | std::vector<IncludedFile> Parser::GetIncludedFiles() const { |
2720 | 0 | const auto it = files_included_per_file_.find(file_being_parsed_); |
2721 | 0 | if (it == files_included_per_file_.end()) { |
2722 | 0 | return {}; |
2723 | 0 | } |
2724 | | |
2725 | 0 | return {it->second.cbegin(), it->second.cend()}; |
2726 | 0 | } |
2727 | | |
2728 | 0 | bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions& opts) { |
2729 | 0 | static FLATBUFFERS_CONSTEXPR unsigned long supported_langs = |
2730 | 0 | IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster | |
2731 | 0 | IDLOptions::kKotlin | IDLOptions::kKotlinKmp | IDLOptions::kCpp | |
2732 | 0 | IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kTs | |
2733 | 0 | IDLOptions::kBinary | IDLOptions::kGo | IDLOptions::kPython | |
2734 | 0 | IDLOptions::kJson | IDLOptions::kNim; |
2735 | 0 | unsigned long langs = opts.lang_to_generate; |
2736 | 0 | return (langs > 0 && langs < IDLOptions::kMAX) && !(langs & ~supported_langs); |
2737 | 0 | } |
2738 | 0 | bool Parser::SupportsOptionalScalars() const { |
2739 | | // Check in general if a language isn't specified. |
2740 | 0 | return opts.lang_to_generate == 0 || SupportsOptionalScalars(opts); |
2741 | 0 | } |
2742 | | |
2743 | 0 | bool Parser::SupportsDefaultVectorsAndStrings() const { |
2744 | 0 | static FLATBUFFERS_CONSTEXPR unsigned long supported_langs = |
2745 | 0 | IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim; |
2746 | 0 | return !(opts.lang_to_generate & ~supported_langs); |
2747 | 0 | } |
2748 | | |
2749 | 0 | bool Parser::SupportsAdvancedUnionFeatures() const { |
2750 | 0 | return (opts.lang_to_generate & |
2751 | 0 | ~(IDLOptions::kCpp | IDLOptions::kTs | IDLOptions::kPhp | |
2752 | 0 | IDLOptions::kJava | IDLOptions::kCSharp | IDLOptions::kKotlin | |
2753 | 0 | IDLOptions::kBinary | IDLOptions::kSwift | IDLOptions::kNim | |
2754 | 0 | IDLOptions::kJson | IDLOptions::kKotlinKmp)) == 0; |
2755 | 0 | } |
2756 | | |
2757 | 0 | bool Parser::SupportsAdvancedArrayFeatures() const { |
2758 | 0 | return (opts.lang_to_generate & |
2759 | 0 | ~(IDLOptions::kCpp | IDLOptions::kPython | IDLOptions::kJava | |
2760 | 0 | IDLOptions::kCSharp | IDLOptions::kJsonSchema | IDLOptions::kJson | |
2761 | 0 | IDLOptions::kBinary | IDLOptions::kRust | IDLOptions::kTs)) == 0; |
2762 | 0 | } |
2763 | | |
2764 | 0 | bool Parser::Supports64BitOffsets() const { |
2765 | 0 | return (opts.lang_to_generate & |
2766 | 0 | ~(IDLOptions::kCpp | IDLOptions::kJson | IDLOptions::kBinary)) == 0; |
2767 | 0 | } |
2768 | | |
2769 | 0 | bool Parser::SupportsUnionUnderlyingType() const { |
2770 | 0 | return (opts.lang_to_generate & |
2771 | 0 | ~(IDLOptions::kCpp | IDLOptions::kTs | IDLOptions::kBinary)) == 0; |
2772 | 0 | } |
2773 | | |
2774 | 46 | Namespace* Parser::UniqueNamespace(Namespace* ns) { |
2775 | 114 | for (auto it = namespaces_.begin(); it != namespaces_.end(); ++it) { |
2776 | 114 | if (ns->components == (*it)->components) { |
2777 | 46 | delete ns; |
2778 | 46 | return *it; |
2779 | 46 | } |
2780 | 114 | } |
2781 | 0 | namespaces_.push_back(ns); |
2782 | 0 | return ns; |
2783 | 46 | } |
2784 | | |
2785 | 46 | std::string Parser::UnqualifiedName(const std::string& full_qualified_name) { |
2786 | 46 | Namespace* ns = new Namespace(); |
2787 | | |
2788 | 46 | std::size_t current, previous = 0; |
2789 | 46 | current = full_qualified_name.find('.'); |
2790 | 132 | while (current != std::string::npos) { |
2791 | 86 | ns->components.push_back( |
2792 | 86 | full_qualified_name.substr(previous, current - previous)); |
2793 | 86 | previous = current + 1; |
2794 | 86 | current = full_qualified_name.find('.', previous); |
2795 | 86 | } |
2796 | 46 | current_namespace_ = UniqueNamespace(ns); |
2797 | 46 | return full_qualified_name.substr(previous, current - previous); |
2798 | 46 | } |
2799 | | |
2800 | 6 | CheckedError Parser::ParseDecl(const char* filename) { |
2801 | 6 | std::vector<std::string> dc = doc_comment_; |
2802 | 6 | bool fixed = IsIdent("struct"); |
2803 | 6 | if (!fixed && !IsIdent("table")) return Error("declaration expected"); |
2804 | 0 | NEXT(); |
2805 | 0 | std::string name = attribute_; |
2806 | 0 | EXPECT(kTokenIdentifier); |
2807 | 0 | StructDef* struct_def; |
2808 | 0 | ECHECK(StartStruct(name, &struct_def)); |
2809 | 0 | struct_def->doc_comment = dc; |
2810 | 0 | struct_def->fixed = fixed; |
2811 | 0 | if (filename && !opts.project_root.empty()) { |
2812 | 0 | struct_def->declaration_file = &GetPooledString(FilePath( |
2813 | 0 | opts.project_root, filename, opts.binary_schema_absolute_paths)); |
2814 | 0 | } |
2815 | 0 | ECHECK(ParseMetaData(&struct_def->attributes)); |
2816 | 0 | struct_def->sortbysize = |
2817 | 0 | struct_def->attributes.Lookup("original_order") == nullptr && !fixed; |
2818 | 0 | EXPECT('{'); |
2819 | 0 | while (token_ != '}') ECHECK(ParseField(*struct_def)); |
2820 | 0 | if (fixed) { |
2821 | 0 | const auto force_align = struct_def->attributes.Lookup("force_align"); |
2822 | 0 | if (force_align) { |
2823 | 0 | size_t align; |
2824 | 0 | ECHECK(ParseAlignAttribute(force_align->constant, struct_def->minalign, |
2825 | 0 | &align)); |
2826 | 0 | struct_def->minalign = align; |
2827 | 0 | } |
2828 | 0 | if (!struct_def->bytesize) return Error("size 0 structs not allowed"); |
2829 | 0 | } |
2830 | 0 | struct_def->PadLastField(struct_def->minalign); |
2831 | | // Check if this is a table that has manual id assignments |
2832 | 0 | auto& fields = struct_def->fields.vec; |
2833 | 0 | if (!fixed && fields.size()) { |
2834 | 0 | size_t num_id_fields = 0; |
2835 | 0 | for (auto it = fields.begin(); it != fields.end(); ++it) { |
2836 | 0 | if ((*it)->attributes.Lookup("id")) num_id_fields++; |
2837 | 0 | } |
2838 | | // If any fields have ids.. |
2839 | 0 | if (num_id_fields || opts.require_explicit_ids) { |
2840 | | // Then all fields must have them. |
2841 | 0 | if (num_id_fields != fields.size()) { |
2842 | 0 | if (opts.require_explicit_ids) { |
2843 | 0 | return Error( |
2844 | 0 | "all fields must have an 'id' attribute when " |
2845 | 0 | "--require-explicit-ids is used"); |
2846 | 0 | } else { |
2847 | 0 | return Error( |
2848 | 0 | "either all fields or no fields must have an 'id' attribute"); |
2849 | 0 | } |
2850 | 0 | } |
2851 | | // Simply sort by id, then the fields are the same as if no ids had |
2852 | | // been specified. |
2853 | 0 | std::sort(fields.begin(), fields.end(), compareFieldDefs); |
2854 | | // Verify we have a contiguous set, and reassign vtable offsets. |
2855 | 0 | FLATBUFFERS_ASSERT(fields.size() <= |
2856 | 0 | flatbuffers::numeric_limits<voffset_t>::max()); |
2857 | 0 | for (voffset_t i = 0; i < static_cast<voffset_t>(fields.size()); i++) { |
2858 | 0 | auto& field = *fields[i]; |
2859 | 0 | const auto& id_str = field.attributes.Lookup("id")->constant; |
2860 | | |
2861 | | // Metadata values have a dynamic type, they can be `float`, 'int', or |
2862 | | // 'string`. |
2863 | | // The FieldIndexToOffset(i) expects the voffset_t so `id` is limited by |
2864 | | // this type. |
2865 | 0 | voffset_t id = 0; |
2866 | 0 | const auto done = !atot(id_str.c_str(), *this, &id).Check(); |
2867 | 0 | if (!done) |
2868 | 0 | return Error("field id\'s must be non-negative number, field: " + |
2869 | 0 | field.name + ", id: " + id_str); |
2870 | 0 | if (i != id) |
2871 | 0 | return Error("field id\'s must be consecutive from 0, id " + |
2872 | 0 | NumToString(i) + " missing or set twice, field: " + |
2873 | 0 | field.name + ", id: " + id_str); |
2874 | 0 | field.value.offset = FieldIndexToOffset(i); |
2875 | 0 | } |
2876 | 0 | } |
2877 | 0 | } |
2878 | | |
2879 | 0 | ECHECK( |
2880 | 0 | CheckClash(fields, struct_def, UnionTypeFieldSuffix(), BASE_TYPE_UNION)); |
2881 | 0 | ECHECK(CheckClash(fields, struct_def, "Type", BASE_TYPE_UNION)); |
2882 | 0 | ECHECK(CheckClash(fields, struct_def, "_length", BASE_TYPE_VECTOR)); |
2883 | 0 | ECHECK(CheckClash(fields, struct_def, "Length", BASE_TYPE_VECTOR)); |
2884 | 0 | ECHECK(CheckClash(fields, struct_def, "_byte_vector", BASE_TYPE_STRING)); |
2885 | 0 | ECHECK(CheckClash(fields, struct_def, "ByteVector", BASE_TYPE_STRING)); |
2886 | 0 | EXPECT('}'); |
2887 | 0 | const auto qualified_name = |
2888 | 0 | current_namespace_->GetFullyQualifiedName(struct_def->name); |
2889 | 0 | if (types_.Add(qualified_name, |
2890 | 0 | new Type(BASE_TYPE_STRUCT, struct_def, nullptr))) |
2891 | 0 | return Error("datatype already exists: " + qualified_name); |
2892 | 0 | return NoError(); |
2893 | 0 | } |
2894 | | |
2895 | 0 | CheckedError Parser::ParseService(const char* filename) { |
2896 | 0 | std::vector<std::string> service_comment = doc_comment_; |
2897 | 0 | NEXT(); |
2898 | 0 | auto service_name = attribute_; |
2899 | 0 | EXPECT(kTokenIdentifier); |
2900 | 0 | auto& service_def = *new ServiceDef(); |
2901 | 0 | service_def.name = service_name; |
2902 | 0 | service_def.file = file_being_parsed_; |
2903 | 0 | service_def.doc_comment = service_comment; |
2904 | 0 | service_def.defined_namespace = current_namespace_; |
2905 | 0 | if (filename != nullptr && !opts.project_root.empty()) { |
2906 | 0 | service_def.declaration_file = &GetPooledString(FilePath( |
2907 | 0 | opts.project_root, filename, opts.binary_schema_absolute_paths)); |
2908 | 0 | } |
2909 | 0 | if (services_.Add(current_namespace_->GetFullyQualifiedName(service_name), |
2910 | 0 | &service_def)) |
2911 | 0 | return Error("service already exists: " + service_name); |
2912 | 0 | ECHECK(ParseMetaData(&service_def.attributes)); |
2913 | 0 | EXPECT('{'); |
2914 | 0 | do { |
2915 | 0 | std::vector<std::string> doc_comment = doc_comment_; |
2916 | 0 | auto rpc_name = attribute_; |
2917 | 0 | EXPECT(kTokenIdentifier); |
2918 | 0 | EXPECT('('); |
2919 | 0 | Type reqtype, resptype; |
2920 | 0 | ECHECK(ParseTypeIdent(reqtype)); |
2921 | 0 | EXPECT(')'); |
2922 | 0 | EXPECT(':'); |
2923 | 0 | ECHECK(ParseTypeIdent(resptype)); |
2924 | 0 | if (reqtype.base_type != BASE_TYPE_STRUCT || reqtype.struct_def->fixed || |
2925 | 0 | resptype.base_type != BASE_TYPE_STRUCT || resptype.struct_def->fixed) |
2926 | 0 | return Error("rpc request and response types must be tables"); |
2927 | 0 | auto& rpc = *new RPCCall(); |
2928 | 0 | rpc.name = rpc_name; |
2929 | 0 | rpc.request = reqtype.struct_def; |
2930 | 0 | rpc.response = resptype.struct_def; |
2931 | 0 | rpc.doc_comment = doc_comment; |
2932 | 0 | if (service_def.calls.Add(rpc_name, &rpc)) |
2933 | 0 | return Error("rpc already exists: " + rpc_name); |
2934 | 0 | ECHECK(ParseMetaData(&rpc.attributes)); |
2935 | 0 | EXPECT(';'); |
2936 | 0 | } while (token_ != '}'); |
2937 | 0 | NEXT(); |
2938 | 0 | return NoError(); |
2939 | 0 | } |
2940 | | |
2941 | 0 | bool Parser::SetRootType(const char* name) { |
2942 | 0 | root_struct_def_ = LookupStruct(name); |
2943 | 0 | if (!root_struct_def_) |
2944 | 0 | root_struct_def_ = |
2945 | 0 | LookupStruct(current_namespace_->GetFullyQualifiedName(name)); |
2946 | 0 | return root_struct_def_ != nullptr; |
2947 | 0 | } |
2948 | | |
2949 | 0 | void Parser::MarkGenerated() { |
2950 | | // This function marks all existing definitions as having already |
2951 | | // been generated, which signals no code for included files should be |
2952 | | // generated. |
2953 | 0 | for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) { |
2954 | 0 | (*it)->generated = true; |
2955 | 0 | } |
2956 | 0 | for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) { |
2957 | 0 | if (!(*it)->predecl) { |
2958 | 0 | (*it)->generated = true; |
2959 | 0 | } |
2960 | 0 | } |
2961 | 0 | for (auto it = services_.vec.begin(); it != services_.vec.end(); ++it) { |
2962 | 0 | (*it)->generated = true; |
2963 | 0 | } |
2964 | 0 | } |
2965 | | |
2966 | 0 | CheckedError Parser::ParseNamespace() { |
2967 | 0 | NEXT(); |
2968 | 0 | auto ns = new Namespace(); |
2969 | 0 | namespaces_.push_back(ns); // Store it here to not leak upon error. |
2970 | 0 | if (token_ != ';') { |
2971 | 0 | for (;;) { |
2972 | 0 | ns->components.push_back(attribute_); |
2973 | 0 | EXPECT(kTokenIdentifier); |
2974 | 0 | if (Is('.')) NEXT() else break; |
2975 | 0 | } |
2976 | 0 | } |
2977 | 0 | namespaces_.pop_back(); |
2978 | 0 | current_namespace_ = UniqueNamespace(ns); |
2979 | 0 | EXPECT(';'); |
2980 | 0 | return NoError(); |
2981 | 0 | } |
2982 | | |
2983 | | // Best effort parsing of .proto declarations, with the aim to turn them |
2984 | | // in the closest corresponding FlatBuffer equivalent. |
2985 | | // We parse everything as identifiers instead of keywords, since we don't |
2986 | | // want protobuf keywords to become invalid identifiers in FlatBuffers. |
2987 | 0 | CheckedError Parser::ParseProtoDecl() { |
2988 | 0 | bool isextend = IsIdent("extend"); |
2989 | 0 | if (IsIdent("package")) { |
2990 | | // These are identical in syntax to FlatBuffer's namespace decl. |
2991 | 0 | ECHECK(ParseNamespace()); |
2992 | 0 | } else if (IsIdent("message") || isextend) { |
2993 | 0 | std::vector<std::string> struct_comment = doc_comment_; |
2994 | 0 | NEXT(); |
2995 | 0 | StructDef* struct_def = nullptr; |
2996 | 0 | Namespace* parent_namespace = nullptr; |
2997 | 0 | if (isextend) { |
2998 | 0 | if (Is('.')) NEXT(); // qualified names may start with a . ? |
2999 | 0 | auto id = attribute_; |
3000 | 0 | EXPECT(kTokenIdentifier); |
3001 | 0 | ECHECK(ParseNamespacing(&id, nullptr)); |
3002 | 0 | struct_def = LookupCreateStruct(id, false); |
3003 | 0 | if (!struct_def) |
3004 | 0 | return Error("cannot extend unknown message type: " + id); |
3005 | 0 | } else { |
3006 | 0 | std::string name = attribute_; |
3007 | 0 | EXPECT(kTokenIdentifier); |
3008 | 0 | ECHECK(StartStruct(name, &struct_def)); |
3009 | | // Since message definitions can be nested, we create a new namespace. |
3010 | 0 | auto ns = new Namespace(); |
3011 | | // Copy of current namespace. |
3012 | 0 | *ns = *current_namespace_; |
3013 | | // But with current message name. |
3014 | 0 | ns->components.push_back(name); |
3015 | 0 | ns->from_table++; |
3016 | 0 | parent_namespace = current_namespace_; |
3017 | 0 | current_namespace_ = UniqueNamespace(ns); |
3018 | 0 | } |
3019 | 0 | struct_def->doc_comment = struct_comment; |
3020 | 0 | ECHECK(ParseProtoFields(struct_def, isextend, false)); |
3021 | 0 | if (!isextend) { |
3022 | 0 | current_namespace_ = parent_namespace; |
3023 | 0 | } |
3024 | 0 | if (Is(';')) NEXT(); |
3025 | 0 | } else if (IsIdent("enum")) { |
3026 | | // These are almost the same, just with different terminator: |
3027 | 0 | EnumDef* enum_def; |
3028 | 0 | ECHECK(ParseEnum(false, &enum_def, nullptr)); |
3029 | 0 | if (Is(';')) NEXT(); |
3030 | | // Temp: remove any duplicates, as .fbs files can't handle them. |
3031 | 0 | enum_def->RemoveDuplicates(); |
3032 | 0 | } else if (IsIdent("syntax")) { // Skip these. |
3033 | 0 | NEXT(); |
3034 | 0 | EXPECT('='); |
3035 | 0 | EXPECT(kTokenStringConstant); |
3036 | 0 | EXPECT(';'); |
3037 | 0 | } else if (IsIdent("option")) { // Skip these. |
3038 | 0 | ECHECK(ParseProtoOption()); |
3039 | 0 | EXPECT(';'); |
3040 | 0 | } else if (IsIdent("service")) { // Skip these. |
3041 | 0 | NEXT(); |
3042 | 0 | EXPECT(kTokenIdentifier); |
3043 | 0 | ECHECK(ParseProtoCurliesOrIdent()); |
3044 | 0 | } else { |
3045 | 0 | return Error("don\'t know how to parse .proto declaration starting with " + |
3046 | 0 | TokenToStringId(token_)); |
3047 | 0 | } |
3048 | 0 | return NoError(); |
3049 | 0 | } |
3050 | | |
3051 | | CheckedError Parser::StartEnum(const std::string& name, bool is_union, |
3052 | 0 | EnumDef** dest) { |
3053 | 0 | auto& enum_def = *new EnumDef(); |
3054 | 0 | enum_def.name = name; |
3055 | 0 | enum_def.file = file_being_parsed_; |
3056 | 0 | enum_def.doc_comment = doc_comment_; |
3057 | 0 | enum_def.is_union = is_union; |
3058 | 0 | enum_def.defined_namespace = current_namespace_; |
3059 | 0 | const auto qualified_name = current_namespace_->GetFullyQualifiedName(name); |
3060 | 0 | if (enums_.Add(qualified_name, &enum_def)) |
3061 | 0 | return Error("enum already exists: " + qualified_name); |
3062 | 0 | enum_def.underlying_type.base_type = |
3063 | 0 | is_union ? BASE_TYPE_UTYPE : BASE_TYPE_INT; |
3064 | 0 | enum_def.underlying_type.enum_def = &enum_def; |
3065 | 0 | if (dest) *dest = &enum_def; |
3066 | 0 | return NoError(); |
3067 | 0 | } |
3068 | | |
3069 | | CheckedError Parser::ParseProtoFields(StructDef* struct_def, bool isextend, |
3070 | 0 | bool inside_oneof) { |
3071 | 0 | EXPECT('{'); |
3072 | 0 | while (token_ != '}') { |
3073 | 0 | if (IsIdent("message") || IsIdent("extend") || IsIdent("enum")) { |
3074 | | // Nested declarations. |
3075 | 0 | ECHECK(ParseProtoDecl()); |
3076 | 0 | } else if (IsIdent("extensions")) { // Skip these. |
3077 | 0 | NEXT(); |
3078 | 0 | EXPECT(kTokenIntegerConstant); |
3079 | 0 | if (Is(kTokenIdentifier)) { |
3080 | 0 | NEXT(); // to |
3081 | 0 | NEXT(); // num |
3082 | 0 | } |
3083 | 0 | EXPECT(';'); |
3084 | 0 | } else if (IsIdent("option")) { // Skip these. |
3085 | 0 | ECHECK(ParseProtoOption()); |
3086 | 0 | EXPECT(';'); |
3087 | 0 | } else if (IsIdent("reserved")) { // Skip these. |
3088 | | /** |
3089 | | * Reserved proto ids can be comma seperated (e.g. 1,2,4,5;) |
3090 | | * or range based (e.g. 9 to 11;) |
3091 | | * or combination of them (e.g. 1,2,9 to 11,4,5;) |
3092 | | * It will be ended by a semicolon. |
3093 | | */ |
3094 | 0 | NEXT(); |
3095 | 0 | bool range = false; |
3096 | 0 | voffset_t from = 0; |
3097 | |
|
3098 | 0 | while (!Is(';')) { |
3099 | 0 | if (token_ == kTokenIntegerConstant) { |
3100 | 0 | voffset_t attribute = 0; |
3101 | 0 | bool done = StringToNumber(attribute_.c_str(), &attribute); |
3102 | 0 | if (!done) |
3103 | 0 | return Error("Protobuf has non positive number in reserved ids"); |
3104 | | |
3105 | 0 | if (range) { |
3106 | 0 | for (voffset_t id = from + 1; id <= attribute; id++) |
3107 | 0 | struct_def->reserved_ids.push_back(id); |
3108 | |
|
3109 | 0 | range = false; |
3110 | 0 | } else { |
3111 | 0 | struct_def->reserved_ids.push_back(attribute); |
3112 | 0 | } |
3113 | |
|
3114 | 0 | from = attribute; |
3115 | 0 | } |
3116 | | |
3117 | 0 | if (attribute_ == "to") range = true; |
3118 | |
|
3119 | 0 | NEXT(); |
3120 | 0 | } // A variety of formats, just skip. |
3121 | | |
3122 | 0 | NEXT(); |
3123 | 0 | } else if (IsIdent("map")) { |
3124 | 0 | ECHECK(ParseProtoMapField(struct_def)); |
3125 | 0 | } else { |
3126 | 0 | std::vector<std::string> field_comment = doc_comment_; |
3127 | | // Parse the qualifier. |
3128 | 0 | bool required = false; |
3129 | 0 | bool repeated = false; |
3130 | 0 | bool oneof = false; |
3131 | 0 | if (!inside_oneof) { |
3132 | 0 | if (IsIdent("optional")) { |
3133 | | // This is the default. |
3134 | 0 | NEXT(); |
3135 | 0 | } else if (IsIdent("required")) { |
3136 | 0 | required = true; |
3137 | 0 | NEXT(); |
3138 | 0 | } else if (IsIdent("repeated")) { |
3139 | 0 | repeated = true; |
3140 | 0 | NEXT(); |
3141 | 0 | } else if (IsIdent("oneof")) { |
3142 | 0 | oneof = true; |
3143 | 0 | NEXT(); |
3144 | 0 | } else { |
3145 | | // can't error, proto3 allows decls without any of the above. |
3146 | 0 | } |
3147 | 0 | } |
3148 | 0 | StructDef* anonymous_struct = nullptr; |
3149 | 0 | EnumDef* oneof_union = nullptr; |
3150 | 0 | Type type; |
3151 | 0 | if (IsIdent("group") || oneof) { |
3152 | 0 | if (!oneof) NEXT(); |
3153 | 0 | if (oneof && opts.proto_oneof_union) { |
3154 | 0 | auto name = ConvertCase(attribute_, Case::kUpperCamel) + "Union"; |
3155 | 0 | ECHECK(StartEnum(name, true, &oneof_union)); |
3156 | 0 | type = Type(BASE_TYPE_UNION, nullptr, oneof_union); |
3157 | 0 | } else { |
3158 | 0 | auto name = "Anonymous" + NumToString(anonymous_counter_++); |
3159 | 0 | ECHECK(StartStruct(name, &anonymous_struct)); |
3160 | 0 | type = Type(BASE_TYPE_STRUCT, anonymous_struct); |
3161 | 0 | } |
3162 | 0 | } else { |
3163 | 0 | ECHECK(ParseTypeFromProtoType(&type)); |
3164 | 0 | } |
3165 | | // Repeated elements get mapped to a vector. |
3166 | 0 | if (repeated) { |
3167 | 0 | type.element = type.base_type; |
3168 | 0 | type.base_type = BASE_TYPE_VECTOR; |
3169 | 0 | if (type.element == BASE_TYPE_VECTOR) { |
3170 | | // We have a vector or vectors, which FlatBuffers doesn't support. |
3171 | | // For now make it a vector of string (since the source is likely |
3172 | | // "repeated bytes"). |
3173 | | // TODO(wvo): A better solution would be to wrap this in a table. |
3174 | 0 | type.element = BASE_TYPE_STRING; |
3175 | 0 | } |
3176 | 0 | } |
3177 | 0 | std::string name = attribute_; |
3178 | 0 | EXPECT(kTokenIdentifier); |
3179 | 0 | std::string proto_field_id; |
3180 | 0 | if (!oneof) { |
3181 | | // Parse the field id. Since we're just translating schemas, not |
3182 | | // any kind of binary compatibility, we can safely ignore these, and |
3183 | | // assign our own. |
3184 | 0 | EXPECT('='); |
3185 | 0 | proto_field_id = attribute_; |
3186 | 0 | EXPECT(kTokenIntegerConstant); |
3187 | 0 | } |
3188 | 0 | FieldDef* field = nullptr; |
3189 | 0 | if (isextend) { |
3190 | | // We allow a field to be re-defined when extending. |
3191 | | // TODO: are there situations where that is problematic? |
3192 | 0 | field = struct_def->fields.Lookup(name); |
3193 | 0 | } |
3194 | 0 | if (!field) ECHECK(AddField(*struct_def, name, type, &field)); |
3195 | 0 | field->doc_comment = field_comment; |
3196 | 0 | if (!proto_field_id.empty() || oneof) { |
3197 | 0 | auto val = new Value(); |
3198 | 0 | val->constant = proto_field_id; |
3199 | 0 | field->attributes.Add("id", val); |
3200 | 0 | } |
3201 | 0 | if (!IsScalar(type.base_type) && required) { |
3202 | 0 | field->presence = FieldDef::kRequired; |
3203 | 0 | } |
3204 | | // See if there's a default specified. |
3205 | 0 | if (Is('[')) { |
3206 | 0 | NEXT(); |
3207 | 0 | for (;;) { |
3208 | 0 | auto key = attribute_; |
3209 | 0 | ECHECK(ParseProtoKey()); |
3210 | 0 | EXPECT('='); |
3211 | 0 | auto val = attribute_; |
3212 | 0 | ECHECK(ParseProtoCurliesOrIdent()); |
3213 | 0 | if (key == "default") { |
3214 | | // Temp: skip non-numeric and non-boolean defaults (enums). |
3215 | 0 | auto numeric = strpbrk(val.c_str(), "0123456789-+."); |
3216 | 0 | if (IsFloat(type.base_type) && |
3217 | 0 | (val == "inf" || val == "+inf" || val == "-inf")) { |
3218 | | // Prefer to be explicit with +inf. |
3219 | 0 | field->value.constant = val == "inf" ? "+inf" : val; |
3220 | 0 | } else if (IsScalar(type.base_type) && numeric == val.c_str()) { |
3221 | 0 | field->value.constant = val; |
3222 | 0 | } else if (val == "true") { |
3223 | 0 | field->value.constant = val; |
3224 | 0 | } // "false" is default, no need to handle explicitly. |
3225 | 0 | } else if (key == "deprecated") { |
3226 | 0 | field->deprecated = val == "true"; |
3227 | 0 | } |
3228 | 0 | if (!Is(',')) break; |
3229 | 0 | NEXT(); |
3230 | 0 | } |
3231 | 0 | EXPECT(']'); |
3232 | 0 | } |
3233 | 0 | if (anonymous_struct) { |
3234 | 0 | ECHECK(ParseProtoFields(anonymous_struct, false, oneof)); |
3235 | 0 | if (Is(';')) NEXT(); |
3236 | 0 | } else if (oneof_union) { |
3237 | | // Parse into a temporary StructDef, then transfer fields into an |
3238 | | // EnumDef describing the oneof as a union. |
3239 | 0 | StructDef oneof_struct; |
3240 | 0 | ECHECK(ParseProtoFields(&oneof_struct, false, oneof)); |
3241 | 0 | if (Is(';')) NEXT(); |
3242 | 0 | for (auto field_it = oneof_struct.fields.vec.begin(); |
3243 | 0 | field_it != oneof_struct.fields.vec.end(); ++field_it) { |
3244 | 0 | const auto& oneof_field = **field_it; |
3245 | 0 | const auto& oneof_type = oneof_field.value.type; |
3246 | 0 | if (oneof_type.base_type != BASE_TYPE_STRUCT || |
3247 | 0 | !oneof_type.struct_def || oneof_type.struct_def->fixed) |
3248 | 0 | return Error("oneof '" + name + |
3249 | 0 | "' cannot be mapped to a union because member '" + |
3250 | 0 | oneof_field.name + "' is not a table type."); |
3251 | 0 | EnumValBuilder evb(*this, *oneof_union); |
3252 | 0 | auto ev = evb.CreateEnumerator(oneof_type.struct_def->name); |
3253 | 0 | ev->union_type = oneof_type; |
3254 | 0 | ev->doc_comment = oneof_field.doc_comment; |
3255 | 0 | ECHECK(evb.AcceptEnumerator(oneof_field.name)); |
3256 | 0 | } |
3257 | 0 | } else { |
3258 | 0 | EXPECT(';'); |
3259 | 0 | } |
3260 | 0 | } |
3261 | 0 | } |
3262 | 0 | NEXT(); |
3263 | 0 | return NoError(); |
3264 | 0 | } |
3265 | | |
3266 | 0 | CheckedError Parser::ParseProtoMapField(StructDef* struct_def) { |
3267 | 0 | NEXT(); |
3268 | 0 | EXPECT('<'); |
3269 | 0 | Type key_type; |
3270 | 0 | ECHECK(ParseType(key_type)); |
3271 | 0 | EXPECT(','); |
3272 | 0 | Type value_type; |
3273 | 0 | ECHECK(ParseType(value_type)); |
3274 | 0 | EXPECT('>'); |
3275 | 0 | auto field_name = attribute_; |
3276 | 0 | NEXT(); |
3277 | 0 | EXPECT('='); |
3278 | 0 | std::string proto_field_id = attribute_; |
3279 | 0 | EXPECT(kTokenIntegerConstant); |
3280 | 0 | EXPECT(';'); |
3281 | |
|
3282 | 0 | auto entry_table_name = ConvertCase(field_name, Case::kUpperCamel) + "Entry"; |
3283 | 0 | StructDef* entry_table; |
3284 | 0 | ECHECK(StartStruct(entry_table_name, &entry_table)); |
3285 | 0 | entry_table->has_key = true; |
3286 | 0 | FieldDef* key_field; |
3287 | 0 | ECHECK(AddField(*entry_table, "key", key_type, &key_field)); |
3288 | 0 | key_field->key = true; |
3289 | 0 | FieldDef* value_field; |
3290 | 0 | ECHECK(AddField(*entry_table, "value", value_type, &value_field)); |
3291 | |
|
3292 | 0 | Type field_type; |
3293 | 0 | field_type.base_type = BASE_TYPE_VECTOR; |
3294 | 0 | field_type.element = BASE_TYPE_STRUCT; |
3295 | 0 | field_type.struct_def = entry_table; |
3296 | 0 | FieldDef* field; |
3297 | 0 | ECHECK(AddField(*struct_def, field_name, field_type, &field)); |
3298 | 0 | if (!proto_field_id.empty()) { |
3299 | 0 | auto val = new Value(); |
3300 | 0 | val->constant = proto_field_id; |
3301 | 0 | field->attributes.Add("id", val); |
3302 | 0 | } |
3303 | |
|
3304 | 0 | return NoError(); |
3305 | 0 | } |
3306 | | |
3307 | 0 | CheckedError Parser::ParseProtoKey() { |
3308 | 0 | if (token_ == '(') { |
3309 | 0 | NEXT(); |
3310 | | // Skip "(a.b)" style custom attributes. |
3311 | 0 | while (token_ == '.' || token_ == kTokenIdentifier) NEXT(); |
3312 | 0 | EXPECT(')'); |
3313 | 0 | while (Is('.')) { |
3314 | 0 | NEXT(); |
3315 | 0 | EXPECT(kTokenIdentifier); |
3316 | 0 | } |
3317 | 0 | } else { |
3318 | 0 | EXPECT(kTokenIdentifier); |
3319 | 0 | } |
3320 | 0 | return NoError(); |
3321 | 0 | } |
3322 | | |
3323 | 0 | CheckedError Parser::ParseProtoCurliesOrIdent() { |
3324 | 0 | if (Is('{')) { |
3325 | 0 | NEXT(); |
3326 | 0 | for (int nesting = 1; nesting;) { |
3327 | 0 | if (token_ == '{') |
3328 | 0 | nesting++; |
3329 | 0 | else if (token_ == '}') |
3330 | 0 | nesting--; |
3331 | 0 | NEXT(); |
3332 | 0 | } |
3333 | 0 | } else { |
3334 | 0 | NEXT(); // Any single token. |
3335 | 0 | } |
3336 | 0 | return NoError(); |
3337 | 0 | } |
3338 | | |
3339 | 0 | CheckedError Parser::ParseProtoOption() { |
3340 | 0 | NEXT(); |
3341 | 0 | ECHECK(ParseProtoKey()); |
3342 | 0 | EXPECT('='); |
3343 | 0 | ECHECK(ParseProtoCurliesOrIdent()); |
3344 | 0 | return NoError(); |
3345 | 0 | } |
3346 | | |
3347 | | // Parse a protobuf type, and map it to the corresponding FlatBuffer one. |
3348 | 0 | CheckedError Parser::ParseTypeFromProtoType(Type* type) { |
3349 | 0 | struct type_lookup { |
3350 | 0 | const char* proto_type; |
3351 | 0 | BaseType fb_type, element; |
3352 | 0 | }; |
3353 | 0 | static type_lookup lookup[] = {{"float", BASE_TYPE_FLOAT, BASE_TYPE_NONE}, |
3354 | 0 | {"double", BASE_TYPE_DOUBLE, BASE_TYPE_NONE}, |
3355 | 0 | {"int32", BASE_TYPE_INT, BASE_TYPE_NONE}, |
3356 | 0 | {"int64", BASE_TYPE_LONG, BASE_TYPE_NONE}, |
3357 | 0 | {"uint32", BASE_TYPE_UINT, BASE_TYPE_NONE}, |
3358 | 0 | {"uint64", BASE_TYPE_ULONG, BASE_TYPE_NONE}, |
3359 | 0 | {"sint32", BASE_TYPE_INT, BASE_TYPE_NONE}, |
3360 | 0 | {"sint64", BASE_TYPE_LONG, BASE_TYPE_NONE}, |
3361 | 0 | {"fixed32", BASE_TYPE_UINT, BASE_TYPE_NONE}, |
3362 | 0 | {"fixed64", BASE_TYPE_ULONG, BASE_TYPE_NONE}, |
3363 | 0 | {"sfixed32", BASE_TYPE_INT, BASE_TYPE_NONE}, |
3364 | 0 | {"sfixed64", BASE_TYPE_LONG, BASE_TYPE_NONE}, |
3365 | 0 | {"bool", BASE_TYPE_BOOL, BASE_TYPE_NONE}, |
3366 | 0 | {"string", BASE_TYPE_STRING, BASE_TYPE_NONE}, |
3367 | 0 | {"bytes", BASE_TYPE_VECTOR, BASE_TYPE_UCHAR}, |
3368 | 0 | {nullptr, BASE_TYPE_NONE, BASE_TYPE_NONE}}; |
3369 | 0 | for (auto tl = lookup; tl->proto_type; tl++) { |
3370 | 0 | if (attribute_ == tl->proto_type) { |
3371 | 0 | type->base_type = tl->fb_type; |
3372 | 0 | type->element = tl->element; |
3373 | 0 | NEXT(); |
3374 | 0 | return NoError(); |
3375 | 0 | } |
3376 | 0 | } |
3377 | 0 | if (Is('.')) NEXT(); // qualified names may start with a . ? |
3378 | 0 | ECHECK(ParseTypeIdent(*type)); |
3379 | 0 | return NoError(); |
3380 | 0 | } |
3381 | | |
3382 | 1.21M | CheckedError Parser::SkipAnyJsonValue() { |
3383 | 1.21M | ParseDepthGuard depth_guard(this); |
3384 | 1.21M | ECHECK(depth_guard.Check()); |
3385 | | |
3386 | 1.21M | switch (token_) { |
3387 | 92.9k | case '{': { |
3388 | 92.9k | size_t fieldn_outer = 0; |
3389 | 92.9k | return ParseTableDelimiters(fieldn_outer, nullptr, |
3390 | 92.9k | [&](const std::string&, size_t& fieldn, |
3391 | 232k | const StructDef*) -> CheckedError { |
3392 | 232k | ECHECK(SkipAnyJsonValue()); |
3393 | 232k | fieldn++; |
3394 | 232k | return NoError(); |
3395 | 232k | }); |
3396 | 0 | } |
3397 | 370k | case '[': { |
3398 | 370k | size_t count = 0; |
3399 | 370k | return ParseVectorDelimiters( |
3400 | 937k | count, [&](size_t&) -> CheckedError { return SkipAnyJsonValue(); }); |
3401 | 0 | } |
3402 | 110k | case kTokenStringConstant: |
3403 | 341k | case kTokenIntegerConstant: |
3404 | 737k | case kTokenFloatConstant: |
3405 | 737k | NEXT(); |
3406 | 737k | break; |
3407 | 16.9k | default: |
3408 | 16.9k | if (IsIdent("true") || IsIdent("false") || IsIdent("null") || |
3409 | 16.6k | IsIdent("inf")) { |
3410 | 16.6k | NEXT(); |
3411 | 16.6k | } else |
3412 | 314 | return TokenError(); |
3413 | 1.21M | } |
3414 | 754k | return NoError(); |
3415 | 1.21M | } |
3416 | | |
3417 | | CheckedError Parser::ParseFlexBufferNumericConstant( |
3418 | 4.90k | flexbuffers::Builder* builder) { |
3419 | 4.90k | double d; |
3420 | 4.90k | if (!StringToNumber(attribute_.c_str(), &d)) |
3421 | 22 | return Error("unexpected floating-point constant: " + attribute_); |
3422 | 4.88k | builder->Double(d); |
3423 | 4.88k | return NoError(); |
3424 | 4.90k | } |
3425 | | |
3426 | 2.21M | CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder* builder) { |
3427 | 2.21M | ParseDepthGuard depth_guard(this); |
3428 | 2.21M | ECHECK(depth_guard.Check()); |
3429 | | |
3430 | 2.21M | switch (token_) { |
3431 | 74.4k | case '{': { |
3432 | 74.4k | auto start = builder->StartMap(); |
3433 | 74.4k | size_t fieldn_outer = 0; |
3434 | 74.4k | auto err = |
3435 | 74.4k | ParseTableDelimiters(fieldn_outer, nullptr, |
3436 | 74.4k | [&](const std::string& name, size_t& fieldn, |
3437 | 286k | const StructDef*) -> CheckedError { |
3438 | 286k | builder->Key(name); |
3439 | 286k | ECHECK(ParseFlexBufferValue(builder)); |
3440 | 285k | fieldn++; |
3441 | 285k | return NoError(); |
3442 | 286k | }); |
3443 | 74.4k | ECHECK(err); |
3444 | 73.6k | builder->EndMap(start); |
3445 | 73.6k | if (builder->HasDuplicateKeys()) |
3446 | 3.80k | return Error("FlexBuffers map has duplicate keys"); |
3447 | 69.8k | break; |
3448 | 73.6k | } |
3449 | 685k | case '[': { |
3450 | 685k | auto start = builder->StartVector(); |
3451 | 685k | size_t count = 0; |
3452 | 685k | ECHECK(ParseVectorDelimiters(count, [&](size_t&) -> CheckedError { |
3453 | 685k | return ParseFlexBufferValue(builder); |
3454 | 685k | })); |
3455 | 684k | builder->EndVector(start, false, false); |
3456 | 684k | break; |
3457 | 685k | } |
3458 | 18.5k | case kTokenStringConstant: |
3459 | 18.5k | builder->String(attribute_); |
3460 | 18.5k | EXPECT(kTokenStringConstant); |
3461 | 18.5k | break; |
3462 | 718k | case kTokenIntegerConstant: |
3463 | 718k | builder->Int(StringToInt(attribute_.c_str())); |
3464 | 718k | EXPECT(kTokenIntegerConstant); |
3465 | 718k | break; |
3466 | 707k | case kTokenFloatConstant: { |
3467 | 707k | double d; |
3468 | 707k | StringToNumber(attribute_.c_str(), &d); |
3469 | 707k | builder->Double(d); |
3470 | 707k | EXPECT(kTokenFloatConstant); |
3471 | 707k | break; |
3472 | 707k | } |
3473 | 218 | case '-': |
3474 | 1.60k | case '+': { |
3475 | | // `[-+]?(nan|inf|infinity)`, see ParseSingleValue(). |
3476 | 1.60k | const auto sign = static_cast<char>(token_); |
3477 | 1.60k | NEXT(); |
3478 | 1.60k | if (token_ != kTokenIdentifier) |
3479 | 0 | return Error("floating-point constant expected"); |
3480 | 1.60k | attribute_.insert(size_t(0), size_t(1), sign); |
3481 | 1.60k | ECHECK(ParseFlexBufferNumericConstant(builder)); |
3482 | 1.58k | NEXT(); |
3483 | 1.58k | break; |
3484 | 1.58k | } |
3485 | 7.15k | default: |
3486 | 7.15k | if (IsIdent("true")) { |
3487 | 1.21k | builder->Bool(true); |
3488 | 1.21k | NEXT(); |
3489 | 5.94k | } else if (IsIdent("false")) { |
3490 | 989 | builder->Bool(false); |
3491 | 989 | NEXT(); |
3492 | 4.95k | } else if (IsIdent("null")) { |
3493 | 1.37k | builder->Null(); |
3494 | 1.37k | NEXT(); |
3495 | 3.58k | } else if (IsIdent("inf") || IsIdent("infinity") || IsIdent("nan")) { |
3496 | 3.30k | ECHECK(ParseFlexBufferNumericConstant(builder)); |
3497 | 3.30k | NEXT(); |
3498 | 3.30k | } else |
3499 | 281 | return TokenError(); |
3500 | 2.21M | } |
3501 | 2.20M | return NoError(); |
3502 | 2.21M | } |
3503 | | |
3504 | | bool Parser::ParseFlexBuffer(const char* source, const char* source_filename, |
3505 | 0 | flexbuffers::Builder* builder) { |
3506 | 0 | const auto initial_depth = parse_depth_counter_; |
3507 | 0 | (void)initial_depth; |
3508 | 0 | auto ok = !StartParseFile(source, source_filename).Check() && |
3509 | 0 | !ParseFlexBufferValue(builder).Check(); |
3510 | 0 | if (ok) builder->Finish(); |
3511 | 0 | FLATBUFFERS_ASSERT(initial_depth == parse_depth_counter_); |
3512 | 0 | return ok; |
3513 | 0 | } |
3514 | | |
3515 | | bool Parser::Parse(const char* source, const char** include_paths, |
3516 | 12.7k | const char* source_filename) { |
3517 | 12.7k | const auto initial_depth = parse_depth_counter_; |
3518 | 12.7k | (void)initial_depth; |
3519 | 12.7k | bool r; |
3520 | | |
3521 | 12.7k | if (opts.use_flexbuffers) { |
3522 | 0 | r = ParseFlexBuffer(source, source_filename, &flex_builder_); |
3523 | 12.7k | } else { |
3524 | 12.7k | r = !ParseRoot(source, include_paths, source_filename).Check(); |
3525 | 12.7k | } |
3526 | 12.7k | FLATBUFFERS_ASSERT(initial_depth == parse_depth_counter_); |
3527 | 12.7k | return r; |
3528 | 12.7k | } |
3529 | | |
3530 | 20.6k | bool Parser::ParseJson(const char* json, const char* json_filename) { |
3531 | 20.6k | const auto initial_depth = parse_depth_counter_; |
3532 | 20.6k | (void)initial_depth; |
3533 | 20.6k | builder_.Clear(); |
3534 | 20.6k | const auto done = |
3535 | 20.6k | !StartParseFile(json, json_filename).Check() && !DoParseJson().Check(); |
3536 | 20.6k | FLATBUFFERS_ASSERT(initial_depth == parse_depth_counter_); |
3537 | 20.6k | return done; |
3538 | 20.6k | } |
3539 | | |
3540 | 0 | std::ptrdiff_t Parser::BytesConsumed() const { |
3541 | 0 | return std::distance(source_, prev_cursor_); |
3542 | 0 | } |
3543 | | |
3544 | | CheckedError Parser::StartParseFile(const char* source, |
3545 | 33.4k | const char* source_filename) { |
3546 | 33.4k | file_being_parsed_ = source_filename ? source_filename : ""; |
3547 | 33.4k | source_ = source; |
3548 | 33.4k | ResetState(source_); |
3549 | 33.4k | error_.clear(); |
3550 | 33.4k | ECHECK(SkipByteOrderMark()); |
3551 | 33.3k | NEXT(); |
3552 | 32.9k | if (Is(kTokenEof)) return Error("input file is empty"); |
3553 | 32.8k | return NoError(); |
3554 | 32.9k | } |
3555 | | |
3556 | | CheckedError Parser::ParseRoot(const char* source, const char** include_paths, |
3557 | 12.7k | const char* source_filename) { |
3558 | 12.7k | ECHECK(DoParse(source, include_paths, source_filename, nullptr)); |
3559 | | |
3560 | | // Check that all types were defined. |
3561 | 12.7k | for (auto it = structs_.vec.begin(); it != structs_.vec.end();) { |
3562 | 0 | auto& struct_def = **it; |
3563 | 0 | if (struct_def.predecl) { |
3564 | 0 | if (opts.proto_mode) { |
3565 | | // Protos allow enums to be used before declaration, so check if that |
3566 | | // is the case here. |
3567 | 0 | EnumDef* enum_def = nullptr; |
3568 | 0 | for (size_t components = |
3569 | 0 | struct_def.defined_namespace->components.size() + 1; |
3570 | 0 | components && !enum_def; components--) { |
3571 | 0 | auto qualified_name = |
3572 | 0 | struct_def.defined_namespace->GetFullyQualifiedName( |
3573 | 0 | struct_def.name, components - 1); |
3574 | 0 | enum_def = LookupEnum(qualified_name); |
3575 | 0 | } |
3576 | 0 | if (enum_def) { |
3577 | | // This is pretty slow, but a simple solution for now. |
3578 | 0 | auto initial_count = struct_def.refcount; |
3579 | 0 | for (auto struct_it = structs_.vec.begin(); |
3580 | 0 | struct_it != structs_.vec.end(); ++struct_it) { |
3581 | 0 | auto& sd = **struct_it; |
3582 | 0 | for (auto field_it = sd.fields.vec.begin(); |
3583 | 0 | field_it != sd.fields.vec.end(); ++field_it) { |
3584 | 0 | auto& field = **field_it; |
3585 | 0 | if (field.value.type.struct_def == &struct_def) { |
3586 | 0 | field.value.type.struct_def = nullptr; |
3587 | 0 | field.value.type.enum_def = enum_def; |
3588 | 0 | auto& bt = IsVector(field.value.type) |
3589 | 0 | ? field.value.type.element |
3590 | 0 | : field.value.type.base_type; |
3591 | 0 | FLATBUFFERS_ASSERT(bt == BASE_TYPE_STRUCT); |
3592 | 0 | bt = enum_def->underlying_type.base_type; |
3593 | 0 | struct_def.refcount--; |
3594 | 0 | enum_def->refcount++; |
3595 | 0 | } |
3596 | 0 | } |
3597 | 0 | } |
3598 | 0 | if (struct_def.refcount) |
3599 | 0 | return Error("internal: " + NumToString(struct_def.refcount) + "/" + |
3600 | 0 | NumToString(initial_count) + |
3601 | 0 | " use(s) of pre-declaration enum not accounted for: " + |
3602 | 0 | enum_def->name); |
3603 | 0 | structs_.dict.erase(structs_.dict.find(struct_def.name)); |
3604 | 0 | it = structs_.vec.erase(it); |
3605 | 0 | delete &struct_def; |
3606 | 0 | continue; // Skip error. |
3607 | 0 | } |
3608 | 0 | } |
3609 | 0 | auto err = "type referenced but not defined (check namespace): " + |
3610 | 0 | struct_def.name; |
3611 | 0 | if (struct_def.original_location) |
3612 | 0 | err += ", originally at: " + *struct_def.original_location; |
3613 | 0 | return Error(err); |
3614 | 0 | } |
3615 | 0 | ++it; |
3616 | 0 | } |
3617 | | |
3618 | | // This check has to happen here and not earlier, because only now do we |
3619 | | // know for sure what the type of these are. |
3620 | 101k | for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) { |
3621 | 89.1k | auto& enum_def = **it; |
3622 | 89.1k | if (enum_def.is_union) { |
3623 | 38.1k | for (auto val_it = enum_def.Vals().begin(); |
3624 | 190k | val_it != enum_def.Vals().end(); ++val_it) { |
3625 | 152k | auto& val = **val_it; |
3626 | | |
3627 | 152k | if (!(opts.lang_to_generate != 0 && SupportsAdvancedUnionFeatures()) && |
3628 | 152k | (IsStruct(val.union_type) || IsString(val.union_type))) |
3629 | | |
3630 | 0 | return Error( |
3631 | 0 | "only tables can be union elements in the generated language: " + |
3632 | 0 | val.name); |
3633 | 152k | } |
3634 | 38.1k | } |
3635 | 89.1k | } |
3636 | | |
3637 | 12.7k | auto err = CheckPrivateLeak(); |
3638 | 12.7k | if (err.Check()) return err; |
3639 | | |
3640 | | // Parse JSON object only if the scheme has been parsed. |
3641 | 12.7k | if (token_ == '{') { |
3642 | 12.7k | ECHECK(DoParseJson()); |
3643 | 11.7k | } |
3644 | 11.7k | return NoError(); |
3645 | 12.7k | } |
3646 | | |
3647 | 12.7k | CheckedError Parser::CheckPrivateLeak() { |
3648 | 12.7k | if (!opts.no_leak_private_annotations) return NoError(); |
3649 | | // Iterate over all structs/tables to validate we arent leaking |
3650 | | // any private (structs/tables/enums) |
3651 | 0 | for (auto it = structs_.vec.begin(); it != structs_.vec.end(); it++) { |
3652 | 0 | auto& struct_def = **it; |
3653 | 0 | for (auto fld_it = struct_def.fields.vec.begin(); |
3654 | 0 | fld_it != struct_def.fields.vec.end(); ++fld_it) { |
3655 | 0 | auto& field = **fld_it; |
3656 | |
|
3657 | 0 | if (field.value.type.enum_def) { |
3658 | 0 | auto err = |
3659 | 0 | CheckPrivatelyLeakedFields(struct_def, *field.value.type.enum_def); |
3660 | 0 | if (err.Check()) { |
3661 | 0 | return err; |
3662 | 0 | } |
3663 | 0 | } else if (field.value.type.struct_def) { |
3664 | 0 | auto err = CheckPrivatelyLeakedFields(struct_def, |
3665 | 0 | *field.value.type.struct_def); |
3666 | 0 | if (err.Check()) { |
3667 | 0 | return err; |
3668 | 0 | } |
3669 | 0 | } |
3670 | 0 | } |
3671 | 0 | } |
3672 | | // Iterate over all enums to validate we arent leaking |
3673 | | // any private (structs/tables) |
3674 | 0 | for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) { |
3675 | 0 | auto& enum_def = **it; |
3676 | 0 | if (enum_def.is_union) { |
3677 | 0 | for (auto val_it = enum_def.Vals().begin(); |
3678 | 0 | val_it != enum_def.Vals().end(); ++val_it) { |
3679 | 0 | auto& val = **val_it; |
3680 | 0 | if (val.union_type.struct_def) { |
3681 | 0 | auto err = |
3682 | 0 | CheckPrivatelyLeakedFields(enum_def, *val.union_type.struct_def); |
3683 | 0 | if (err.Check()) { |
3684 | 0 | return err; |
3685 | 0 | } |
3686 | 0 | } |
3687 | 0 | } |
3688 | 0 | } |
3689 | 0 | } |
3690 | 0 | return NoError(); |
3691 | 0 | } |
3692 | | |
3693 | | CheckedError Parser::CheckPrivatelyLeakedFields(const Definition& def, |
3694 | 0 | const Definition& value_type) { |
3695 | 0 | if (!opts.no_leak_private_annotations) return NoError(); |
3696 | 0 | const auto is_private = def.attributes.Lookup("private"); |
3697 | 0 | const auto is_field_private = value_type.attributes.Lookup("private"); |
3698 | 0 | if (!is_private && is_field_private) { |
3699 | 0 | return Error( |
3700 | 0 | "Leaking private implementation, verify all objects have similar " |
3701 | 0 | "annotations"); |
3702 | 0 | } |
3703 | 0 | return NoError(); |
3704 | 0 | } |
3705 | | |
3706 | | CheckedError Parser::DoParse(const char* source, const char** include_paths, |
3707 | | const char* source_filename, |
3708 | 12.7k | const char* include_filename) { |
3709 | 12.7k | uint64_t source_hash = 0; |
3710 | 12.7k | if (source_filename) { |
3711 | | // If the file is in-memory, don't include its contents in the hash as we |
3712 | | // won't be able to load them later. |
3713 | 0 | if (FileExists(source_filename)) |
3714 | 0 | source_hash = HashFile(source_filename, source); |
3715 | 0 | else |
3716 | 0 | source_hash = HashFile(source_filename, nullptr); |
3717 | |
|
3718 | 0 | if (included_files_.find(source_hash) == included_files_.end()) { |
3719 | 0 | included_files_[source_hash] = include_filename ? include_filename : ""; |
3720 | 0 | files_included_per_file_[source_filename] = std::set<IncludedFile>(); |
3721 | 0 | } else { |
3722 | 0 | return NoError(); |
3723 | 0 | } |
3724 | 0 | } |
3725 | 12.7k | if (!include_paths) { |
3726 | 12.7k | static const char* current_directory[] = {"", nullptr}; |
3727 | 12.7k | include_paths = current_directory; |
3728 | 12.7k | } |
3729 | 12.7k | field_stack_.clear(); |
3730 | 12.7k | builder_.Clear(); |
3731 | | // Start with a blank namespace just in case this file doesn't have one. |
3732 | 12.7k | current_namespace_ = empty_namespace_; |
3733 | | |
3734 | 12.7k | ECHECK(StartParseFile(source, source_filename)); |
3735 | | |
3736 | | // Includes must come before type declarations: |
3737 | 12.7k | for (;;) { |
3738 | | // Parse pre-include proto statements if any: |
3739 | 12.7k | if (opts.proto_mode && (attribute_ == "option" || attribute_ == "syntax" || |
3740 | 0 | attribute_ == "package")) { |
3741 | 0 | ECHECK(ParseProtoDecl()); |
3742 | 12.7k | } else if (IsIdent("native_include")) { |
3743 | 0 | NEXT(); |
3744 | 0 | native_included_files_.emplace_back(attribute_); |
3745 | 0 | EXPECT(kTokenStringConstant); |
3746 | 0 | EXPECT(';'); |
3747 | 12.7k | } else if (IsIdent("include") || (opts.proto_mode && IsIdent("import"))) { |
3748 | 0 | NEXT(); |
3749 | 0 | if (opts.proto_mode && attribute_ == "public") NEXT(); |
3750 | 0 | auto name = flatbuffers::PosixPath(attribute_.c_str()); |
3751 | 0 | EXPECT(kTokenStringConstant); |
3752 | | // Look for the file relative to the directory of the current file. |
3753 | 0 | std::string filepath; |
3754 | 0 | if (source_filename) { |
3755 | 0 | auto source_file_directory = |
3756 | 0 | flatbuffers::StripFileName(source_filename); |
3757 | 0 | filepath = flatbuffers::ConCatPathFileName(source_file_directory, name); |
3758 | 0 | } |
3759 | 0 | if (filepath.empty() || !FileExists(filepath.c_str())) { |
3760 | | // Look for the file in include_paths. |
3761 | 0 | for (auto paths = include_paths; paths && *paths; paths++) { |
3762 | 0 | filepath = flatbuffers::ConCatPathFileName(*paths, name); |
3763 | 0 | if (FileExists(filepath.c_str())) break; |
3764 | 0 | } |
3765 | 0 | } |
3766 | 0 | if (filepath.empty()) |
3767 | 0 | return Error("unable to locate include file: " + name); |
3768 | 0 | if (source_filename) { |
3769 | 0 | IncludedFile included_file; |
3770 | 0 | included_file.filename = filepath; |
3771 | 0 | included_file.schema_name = name; |
3772 | 0 | files_included_per_file_[source_filename].insert(included_file); |
3773 | 0 | } |
3774 | |
|
3775 | 0 | std::string contents; |
3776 | 0 | bool file_loaded = LoadFile(filepath.c_str(), true, &contents); |
3777 | 0 | if (included_files_.find(HashFile(filepath.c_str(), contents.c_str())) == |
3778 | 0 | included_files_.end()) { |
3779 | | // We found an include file that we have not parsed yet. |
3780 | | // Parse it. |
3781 | 0 | if (!file_loaded) return Error("unable to load include file: " + name); |
3782 | 0 | ECHECK(DoParse(contents.c_str(), include_paths, filepath.c_str(), |
3783 | 0 | name.c_str())); |
3784 | | // We generally do not want to output code for any included files: |
3785 | 0 | if (!opts.generate_all) MarkGenerated(); |
3786 | | // Reset these just in case the included file had them, and the |
3787 | | // parent doesn't. |
3788 | 0 | root_struct_def_ = nullptr; |
3789 | 0 | file_identifier_.clear(); |
3790 | 0 | file_extension_.clear(); |
3791 | | // This is the easiest way to continue this file after an include: |
3792 | | // instead of saving and restoring all the state, we simply start the |
3793 | | // file anew. This will cause it to encounter the same include |
3794 | | // statement again, but this time it will skip it, because it was |
3795 | | // entered into included_files_. |
3796 | | // This is recursive, but only go as deep as the number of include |
3797 | | // statements. |
3798 | 0 | included_files_.erase(source_hash); |
3799 | 0 | return DoParse(source, include_paths, source_filename, |
3800 | 0 | include_filename); |
3801 | 0 | } |
3802 | 0 | EXPECT(';'); |
3803 | 12.7k | } else { |
3804 | 12.7k | break; |
3805 | 12.7k | } |
3806 | 12.7k | } |
3807 | | // Now parse all other kinds of declarations: |
3808 | 12.7k | while (token_ != kTokenEof) { |
3809 | 12.7k | if (opts.proto_mode) { |
3810 | 0 | ECHECK(ParseProtoDecl()); |
3811 | 12.7k | } else if (IsIdent("namespace")) { |
3812 | 0 | ECHECK(ParseNamespace()); |
3813 | 12.7k | } else if (token_ == '{') { |
3814 | 12.7k | return NoError(); |
3815 | 12.7k | } else if (IsIdent("enum")) { |
3816 | 1 | ECHECK(ParseEnum(false, nullptr, source_filename)); |
3817 | 9 | } else if (IsIdent("union")) { |
3818 | 0 | ECHECK(ParseEnum(true, nullptr, source_filename)); |
3819 | 9 | } else if (IsIdent("root_type")) { |
3820 | 0 | NEXT(); |
3821 | 0 | auto root_type = attribute_; |
3822 | 0 | EXPECT(kTokenIdentifier); |
3823 | 0 | ECHECK(ParseNamespacing(&root_type, nullptr)); |
3824 | 0 | if (opts.root_type.empty()) { |
3825 | 0 | if (!SetRootType(root_type.c_str())) |
3826 | 0 | return Error("unknown root type: " + root_type); |
3827 | 0 | if (root_struct_def_->fixed) return Error("root type must be a table"); |
3828 | 0 | } |
3829 | 0 | EXPECT(';'); |
3830 | 9 | } else if (IsIdent("file_identifier")) { |
3831 | 1 | NEXT(); |
3832 | 1 | file_identifier_ = attribute_; |
3833 | 1 | EXPECT(kTokenStringConstant); |
3834 | 0 | if (file_identifier_.length() != flatbuffers::kFileIdentifierLength) |
3835 | 0 | return Error("file_identifier must be exactly " + |
3836 | 0 | NumToString(flatbuffers::kFileIdentifierLength) + |
3837 | 0 | " characters"); |
3838 | 0 | EXPECT(';'); |
3839 | 8 | } else if (IsIdent("file_extension")) { |
3840 | 1 | NEXT(); |
3841 | 1 | file_extension_ = attribute_; |
3842 | 1 | EXPECT(kTokenStringConstant); |
3843 | 0 | EXPECT(';'); |
3844 | 7 | } else if (IsIdent("include")) { |
3845 | 0 | return Error("includes must come before declarations"); |
3846 | 7 | } else if (IsIdent("attribute")) { |
3847 | 1 | NEXT(); |
3848 | 1 | auto name = attribute_; |
3849 | 1 | if (Is(kTokenIdentifier)) { |
3850 | 0 | NEXT(); |
3851 | 1 | } else { |
3852 | 1 | EXPECT(kTokenStringConstant); |
3853 | 0 | } |
3854 | 0 | EXPECT(';'); |
3855 | 0 | known_attributes_[name] = false; |
3856 | 6 | } else if (IsIdent("rpc_service")) { |
3857 | 0 | ECHECK(ParseService(source_filename)); |
3858 | 6 | } else { |
3859 | 6 | ECHECK(ParseDecl(source_filename)); |
3860 | 0 | } |
3861 | 12.7k | } |
3862 | 0 | EXPECT(kTokenEof); |
3863 | 0 | if (opts.warnings_as_errors && has_warning_) { |
3864 | 0 | return Error("treating warnings as errors, failed due to above warnings"); |
3865 | 0 | } |
3866 | 0 | return NoError(); |
3867 | 0 | } |
3868 | | |
3869 | 32.7k | CheckedError Parser::DoParseJson() { |
3870 | 32.7k | if (token_ != '{') { |
3871 | 266 | EXPECT('{'); |
3872 | 32.5k | } else { |
3873 | 32.5k | if (!root_struct_def_) return Error("no root type set to parse json with"); |
3874 | 32.5k | if (builder_.GetSize()) { |
3875 | 0 | return Error("cannot have more than one json object in a file"); |
3876 | 0 | } |
3877 | 32.5k | uoffset_t toff; |
3878 | 32.5k | ECHECK(ParseTable(*root_struct_def_, nullptr, &toff)); |
3879 | 20.0k | if (opts.size_prefixed) { |
3880 | 0 | builder_.FinishSizePrefixed( |
3881 | 0 | Offset<Table>(toff), |
3882 | 0 | file_identifier_.length() ? file_identifier_.c_str() : nullptr); |
3883 | 20.0k | } else { |
3884 | 20.0k | builder_.Finish(Offset<Table>(toff), file_identifier_.length() |
3885 | 20.0k | ? file_identifier_.c_str() |
3886 | 20.0k | : nullptr); |
3887 | 20.0k | } |
3888 | 20.0k | } |
3889 | 20.0k | if (opts.require_json_eof) { |
3890 | | // Check that JSON file doesn't contain more objects or IDL directives. |
3891 | | // Comments after JSON are allowed. |
3892 | 20.0k | EXPECT(kTokenEof); |
3893 | 19.9k | } |
3894 | 19.9k | return NoError(); |
3895 | 20.0k | } |
3896 | | |
3897 | | std::set<std::string> Parser::GetIncludedFilesRecursive( |
3898 | 0 | const std::string& file_name) const { |
3899 | 0 | std::set<std::string> included_files; |
3900 | 0 | std::list<std::string> to_process; |
3901 | |
|
3902 | 0 | if (file_name.empty()) return included_files; |
3903 | 0 | to_process.push_back(file_name); |
3904 | |
|
3905 | 0 | while (!to_process.empty()) { |
3906 | 0 | std::string current = to_process.front(); |
3907 | 0 | to_process.pop_front(); |
3908 | 0 | included_files.insert(current); |
3909 | | |
3910 | | // Workaround the lack of const accessor in C++98 maps. |
3911 | 0 | auto& new_files = |
3912 | 0 | (*const_cast<std::map<std::string, std::set<IncludedFile>>*>( |
3913 | 0 | &files_included_per_file_))[current]; |
3914 | 0 | for (auto it = new_files.begin(); it != new_files.end(); ++it) { |
3915 | 0 | if (included_files.find(it->filename) == included_files.end()) |
3916 | 0 | to_process.push_back(it->filename); |
3917 | 0 | } |
3918 | 0 | } |
3919 | |
|
3920 | 0 | return included_files; |
3921 | 0 | } |
3922 | | |
3923 | | // Schema serialization functionality: |
3924 | | |
3925 | | static flatbuffers::Offset< |
3926 | | flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>> |
3927 | | SerializeAttributesCommon(const SymbolTable<Value>& attributes, |
3928 | 0 | FlatBufferBuilder* builder, const Parser& parser) { |
3929 | 0 | std::vector<flatbuffers::Offset<reflection::KeyValue>> attrs; |
3930 | 0 | for (auto kv = attributes.dict.begin(); kv != attributes.dict.end(); ++kv) { |
3931 | 0 | auto it = parser.known_attributes_.find(kv->first); |
3932 | 0 | FLATBUFFERS_ASSERT(it != parser.known_attributes_.end()); |
3933 | 0 | if (parser.opts.binary_schema_builtins || !it->second) { |
3934 | 0 | auto key = builder->CreateString(kv->first); |
3935 | 0 | auto val = builder->CreateString(kv->second->constant); |
3936 | 0 | attrs.push_back(reflection::CreateKeyValue(*builder, key, val)); |
3937 | 0 | } |
3938 | 0 | } |
3939 | 0 | if (attrs.size()) { |
3940 | 0 | return builder->CreateVectorOfSortedTables(&attrs); |
3941 | 0 | } else { |
3942 | 0 | return 0; |
3943 | 0 | } |
3944 | 0 | } |
3945 | | |
3946 | | static bool DeserializeAttributesCommon( |
3947 | | SymbolTable<Value>& attributes, Parser& parser, |
3948 | 292 | const Vector<Offset<reflection::KeyValue>>* attrs) { |
3949 | 292 | if (attrs == nullptr) return true; |
3950 | 376 | for (uoffset_t i = 0; i < attrs->size(); ++i) { |
3951 | 230 | auto kv = attrs->Get(i); |
3952 | 230 | auto value = new Value(); |
3953 | 230 | if (kv->value()) { |
3954 | 230 | value->constant = kv->value()->str(); |
3955 | 230 | } |
3956 | 230 | if (attributes.Add(kv->key()->str(), value)) { |
3957 | 0 | delete value; |
3958 | 0 | return false; |
3959 | 0 | } |
3960 | 230 | parser.known_attributes_[kv->key()->str()]; |
3961 | 230 | } |
3962 | 146 | return true; |
3963 | 146 | } |
3964 | | |
3965 | 0 | void Parser::Serialize() { |
3966 | 0 | builder_.Clear(); |
3967 | 0 | AssignIndices(structs_.vec); |
3968 | 0 | AssignIndices(enums_.vec); |
3969 | 0 | std::vector<Offset<reflection::Object>> object_offsets; |
3970 | 0 | std::set<std::string> files; |
3971 | 0 | for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) { |
3972 | 0 | auto offset = (*it)->Serialize(&builder_, *this); |
3973 | 0 | object_offsets.push_back(offset); |
3974 | 0 | (*it)->serialized_location = offset.o; |
3975 | 0 | const std::string* file = (*it)->declaration_file; |
3976 | 0 | if (file) files.insert(*file); |
3977 | 0 | } |
3978 | 0 | std::vector<Offset<reflection::Enum>> enum_offsets; |
3979 | 0 | for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) { |
3980 | 0 | auto offset = (*it)->Serialize(&builder_, *this); |
3981 | 0 | enum_offsets.push_back(offset); |
3982 | 0 | const std::string* file = (*it)->declaration_file; |
3983 | 0 | if (file) files.insert(*file); |
3984 | 0 | } |
3985 | 0 | std::vector<Offset<reflection::Service>> service_offsets; |
3986 | 0 | for (auto it = services_.vec.begin(); it != services_.vec.end(); ++it) { |
3987 | 0 | auto offset = (*it)->Serialize(&builder_, *this); |
3988 | 0 | service_offsets.push_back(offset); |
3989 | 0 | const std::string* file = (*it)->declaration_file; |
3990 | 0 | if (file) files.insert(*file); |
3991 | 0 | } |
3992 | | |
3993 | | // Create Schemafiles vector of tables. |
3994 | 0 | flatbuffers::Offset< |
3995 | 0 | flatbuffers::Vector<flatbuffers::Offset<reflection::SchemaFile>>> |
3996 | 0 | schema_files__; |
3997 | 0 | if (!opts.project_root.empty()) { |
3998 | 0 | std::vector<Offset<reflection::SchemaFile>> schema_files; |
3999 | 0 | std::vector<Offset<flatbuffers::String>> included_files; |
4000 | 0 | for (auto f = files_included_per_file_.begin(); |
4001 | 0 | f != files_included_per_file_.end(); f++) { |
4002 | 0 | const auto filename__ = builder_.CreateSharedString(FilePath( |
4003 | 0 | opts.project_root, f->first, opts.binary_schema_absolute_paths)); |
4004 | 0 | for (auto i = f->second.begin(); i != f->second.end(); i++) { |
4005 | 0 | included_files.push_back(builder_.CreateSharedString( |
4006 | 0 | FilePath(opts.project_root, i->filename, |
4007 | 0 | opts.binary_schema_absolute_paths))); |
4008 | 0 | } |
4009 | 0 | const auto included_files__ = builder_.CreateVector(included_files); |
4010 | 0 | included_files.clear(); |
4011 | |
|
4012 | 0 | schema_files.push_back( |
4013 | 0 | reflection::CreateSchemaFile(builder_, filename__, included_files__)); |
4014 | 0 | } |
4015 | 0 | schema_files__ = builder_.CreateVectorOfSortedTables(&schema_files); |
4016 | 0 | } |
4017 | |
|
4018 | 0 | const auto objs__ = builder_.CreateVectorOfSortedTables(&object_offsets); |
4019 | 0 | const auto enum__ = builder_.CreateVectorOfSortedTables(&enum_offsets); |
4020 | 0 | const auto fiid__ = builder_.CreateString(file_identifier_); |
4021 | 0 | const auto fext__ = builder_.CreateString(file_extension_); |
4022 | 0 | const auto serv__ = builder_.CreateVectorOfSortedTables(&service_offsets); |
4023 | 0 | const auto schema_offset = reflection::CreateSchema( |
4024 | 0 | builder_, objs__, enum__, fiid__, fext__, |
4025 | 0 | (root_struct_def_ ? root_struct_def_->serialized_location : 0), serv__, |
4026 | 0 | static_cast<reflection::AdvancedFeatures>(advanced_features_), |
4027 | 0 | schema_files__); |
4028 | 0 | if (opts.size_prefixed) { |
4029 | 0 | builder_.FinishSizePrefixed(schema_offset, reflection::SchemaIdentifier()); |
4030 | 0 | } else { |
4031 | 0 | builder_.Finish(schema_offset, reflection::SchemaIdentifier()); |
4032 | 0 | } |
4033 | 0 | } |
4034 | | |
4035 | | Offset<reflection::Object> StructDef::Serialize(FlatBufferBuilder* builder, |
4036 | 0 | const Parser& parser) const { |
4037 | 0 | std::vector<Offset<reflection::Field>> field_offsets; |
4038 | 0 | for (auto it = fields.vec.begin(); it != fields.vec.end(); ++it) { |
4039 | 0 | field_offsets.push_back((*it)->Serialize( |
4040 | 0 | builder, static_cast<uint16_t>(it - fields.vec.begin()), parser)); |
4041 | 0 | } |
4042 | 0 | const auto qualified_name = defined_namespace->GetFullyQualifiedName(name); |
4043 | 0 | const auto name__ = builder->CreateString(qualified_name); |
4044 | 0 | const auto flds__ = builder->CreateVectorOfSortedTables(&field_offsets); |
4045 | 0 | const auto attr__ = SerializeAttributes(builder, parser); |
4046 | 0 | const auto docs__ = parser.opts.binary_schema_comments && !doc_comment.empty() |
4047 | 0 | ? builder->CreateVectorOfStrings(doc_comment) |
4048 | 0 | : 0; |
4049 | 0 | std::string decl_file_in_project = declaration_file ? *declaration_file : ""; |
4050 | 0 | const auto file__ = builder->CreateSharedString(decl_file_in_project); |
4051 | 0 | return reflection::CreateObject( |
4052 | 0 | *builder, name__, flds__, fixed, static_cast<int>(minalign), |
4053 | 0 | static_cast<int>(bytesize), attr__, docs__, file__); |
4054 | 0 | } |
4055 | | |
4056 | 30 | bool StructDef::Deserialize(Parser& parser, const reflection::Object* object) { |
4057 | 30 | if (!DeserializeAttributes(parser, object->attributes())) return false; |
4058 | 30 | DeserializeDoc(doc_comment, object->documentation()); |
4059 | 30 | name = parser.UnqualifiedName(object->name()->str()); |
4060 | 30 | predecl = false; |
4061 | 30 | sortbysize = attributes.Lookup("original_order") == nullptr && !fixed; |
4062 | 30 | const auto& of = *(object->fields()); |
4063 | 30 | auto indexes = std::vector<uoffset_t>(of.size()); |
4064 | 222 | for (uoffset_t i = 0; i < of.size(); i++) indexes[of.Get(i)->id()] = i; |
4065 | 30 | size_t tmp_struct_size = 0; |
4066 | 222 | for (size_t i = 0; i < indexes.size(); i++) { |
4067 | 192 | auto field = of.Get(indexes[i]); |
4068 | 192 | auto field_def = new FieldDef(); |
4069 | 192 | if (!field_def->Deserialize(parser, field) || |
4070 | 192 | fields.Add(field_def->name, field_def)) { |
4071 | 0 | delete field_def; |
4072 | 0 | return false; |
4073 | 0 | } |
4074 | 192 | if (field_def->key) { |
4075 | 8 | if (has_key) { |
4076 | | // only one field may be set as key |
4077 | 0 | delete field_def; |
4078 | 0 | return false; |
4079 | 0 | } |
4080 | 8 | has_key = true; |
4081 | 8 | } |
4082 | 192 | if (fixed) { |
4083 | | // Recompute padding since that's currently not serialized. |
4084 | 30 | auto size = InlineSize(field_def->value.type); |
4085 | 30 | auto next_field = |
4086 | 30 | i + 1 < indexes.size() ? of.Get(indexes[i + 1]) : nullptr; |
4087 | 30 | tmp_struct_size += size; |
4088 | 30 | field_def->padding = |
4089 | 30 | next_field ? (next_field->offset() - field_def->value.offset) - size |
4090 | 30 | : PaddingBytes(tmp_struct_size, minalign); |
4091 | 30 | tmp_struct_size += field_def->padding; |
4092 | 30 | } |
4093 | 192 | } |
4094 | 30 | FLATBUFFERS_ASSERT(static_cast<int>(tmp_struct_size) == object->bytesize()); |
4095 | 30 | return true; |
4096 | 30 | } |
4097 | | |
4098 | | Offset<reflection::Field> FieldDef::Serialize(FlatBufferBuilder* builder, |
4099 | | uint16_t id, |
4100 | 0 | const Parser& parser) const { |
4101 | 0 | auto name__ = builder->CreateString(name); |
4102 | 0 | auto type__ = value.type.Serialize(builder); |
4103 | 0 | auto attr__ = SerializeAttributes(builder, parser); |
4104 | 0 | auto docs__ = parser.opts.binary_schema_comments && !doc_comment.empty() |
4105 | 0 | ? builder->CreateVectorOfStrings(doc_comment) |
4106 | 0 | : 0; |
4107 | 0 | double d; |
4108 | 0 | StringToNumber(value.constant.c_str(), &d); |
4109 | 0 | return reflection::CreateField( |
4110 | 0 | *builder, name__, type__, id, value.offset, |
4111 | | // Is uint64>max(int64) tested? |
4112 | 0 | IsInteger(value.type.base_type) ? StringToInt(value.constant.c_str()) : 0, |
4113 | | // result may be platform-dependent if underlying is float (not double) |
4114 | 0 | IsFloat(value.type.base_type) ? d : 0.0, deprecated, IsRequired(), key, |
4115 | 0 | attr__, docs__, IsOptional(), static_cast<uint16_t>(padding), offset64); |
4116 | | // TODO: value.constant is almost always "0", we could save quite a bit of |
4117 | | // space by sharing it. Same for common values of value.type. |
4118 | 0 | } |
4119 | | |
4120 | 192 | bool FieldDef::Deserialize(Parser& parser, const reflection::Field* field) { |
4121 | 192 | name = field->name()->str(); |
4122 | 192 | defined_namespace = parser.current_namespace_; |
4123 | 192 | if (!value.type.Deserialize(parser, field->type())) return false; |
4124 | 192 | value.offset = field->offset(); |
4125 | 192 | if (IsInteger(value.type.base_type)) { |
4126 | 80 | value.constant = NumToString(field->default_integer()); |
4127 | 112 | } else if (IsFloat(value.type.base_type)) { |
4128 | 34 | value.constant = FloatToString(field->default_real(), 17); |
4129 | 34 | } |
4130 | 192 | presence = FieldDef::MakeFieldPresence(field->optional(), field->required()); |
4131 | 192 | padding = field->padding(); |
4132 | 192 | key = field->key(); |
4133 | 192 | offset64 = field->offset64(); |
4134 | 192 | if (!DeserializeAttributes(parser, field->attributes())) return false; |
4135 | | // TODO: this should probably be handled by a separate attribute |
4136 | 192 | if (attributes.Lookup("flexbuffer")) { |
4137 | 2 | flexbuffer = true; |
4138 | 2 | parser.uses_flexbuffers_ = true; |
4139 | 2 | if (value.type.base_type != BASE_TYPE_VECTOR || |
4140 | 2 | value.type.element != BASE_TYPE_UCHAR) |
4141 | 0 | return false; |
4142 | 2 | } |
4143 | 192 | if (auto nested = attributes.Lookup("nested_flatbuffer")) { |
4144 | 4 | auto nested_qualified_name = |
4145 | 4 | parser.current_namespace_->GetFullyQualifiedName(nested->constant); |
4146 | 4 | nested_flatbuffer = parser.LookupStruct(nested_qualified_name); |
4147 | 4 | if (!nested_flatbuffer) return false; |
4148 | 4 | } |
4149 | 192 | shared = attributes.Lookup("shared") != nullptr; |
4150 | 192 | DeserializeDoc(doc_comment, field->documentation()); |
4151 | 192 | return true; |
4152 | 192 | } |
4153 | | |
4154 | | Offset<reflection::RPCCall> RPCCall::Serialize(FlatBufferBuilder* builder, |
4155 | 0 | const Parser& parser) const { |
4156 | 0 | auto name__ = builder->CreateString(name); |
4157 | 0 | auto attr__ = SerializeAttributes(builder, parser); |
4158 | 0 | auto docs__ = parser.opts.binary_schema_comments && !doc_comment.empty() |
4159 | 0 | ? builder->CreateVectorOfStrings(doc_comment) |
4160 | 0 | : 0; |
4161 | 0 | return reflection::CreateRPCCall( |
4162 | 0 | *builder, name__, request->serialized_location, |
4163 | 0 | response->serialized_location, attr__, docs__); |
4164 | 0 | } |
4165 | | |
4166 | 8 | bool RPCCall::Deserialize(Parser& parser, const reflection::RPCCall* call) { |
4167 | 8 | name = call->name()->str(); |
4168 | 8 | if (!DeserializeAttributes(parser, call->attributes())) return false; |
4169 | 8 | DeserializeDoc(doc_comment, call->documentation()); |
4170 | 8 | request = parser.structs_.Lookup(call->request()->name()->str()); |
4171 | 8 | response = parser.structs_.Lookup(call->response()->name()->str()); |
4172 | 8 | if (!request || !response) { |
4173 | 0 | return false; |
4174 | 0 | } |
4175 | 8 | return true; |
4176 | 8 | } |
4177 | | |
4178 | | Offset<reflection::Service> ServiceDef::Serialize(FlatBufferBuilder* builder, |
4179 | 0 | const Parser& parser) const { |
4180 | 0 | std::vector<Offset<reflection::RPCCall>> servicecall_offsets; |
4181 | 0 | for (auto it = calls.vec.begin(); it != calls.vec.end(); ++it) { |
4182 | 0 | servicecall_offsets.push_back((*it)->Serialize(builder, parser)); |
4183 | 0 | } |
4184 | 0 | const auto qualified_name = defined_namespace->GetFullyQualifiedName(name); |
4185 | 0 | const auto name__ = builder->CreateString(qualified_name); |
4186 | 0 | const auto call__ = builder->CreateVector(servicecall_offsets); |
4187 | 0 | const auto attr__ = SerializeAttributes(builder, parser); |
4188 | 0 | const auto docs__ = parser.opts.binary_schema_comments && !doc_comment.empty() |
4189 | 0 | ? builder->CreateVectorOfStrings(doc_comment) |
4190 | 0 | : 0; |
4191 | 0 | std::string decl_file_in_project = declaration_file ? *declaration_file : ""; |
4192 | 0 | const auto file__ = builder->CreateSharedString(decl_file_in_project); |
4193 | 0 | return reflection::CreateService(*builder, name__, call__, attr__, docs__, |
4194 | 0 | file__); |
4195 | 0 | } |
4196 | | |
4197 | | bool ServiceDef::Deserialize(Parser& parser, |
4198 | 2 | const reflection::Service* service) { |
4199 | 2 | name = parser.UnqualifiedName(service->name()->str()); |
4200 | 2 | if (service->calls()) { |
4201 | 10 | for (uoffset_t i = 0; i < service->calls()->size(); ++i) { |
4202 | 8 | auto call = new RPCCall(); |
4203 | 8 | if (!call->Deserialize(parser, service->calls()->Get(i)) || |
4204 | 8 | calls.Add(call->name, call)) { |
4205 | 0 | delete call; |
4206 | 0 | return false; |
4207 | 0 | } |
4208 | 8 | } |
4209 | 2 | } |
4210 | 2 | if (!DeserializeAttributes(parser, service->attributes())) return false; |
4211 | 2 | DeserializeDoc(doc_comment, service->documentation()); |
4212 | 2 | return true; |
4213 | 2 | } |
4214 | | |
4215 | | Offset<reflection::Enum> EnumDef::Serialize(FlatBufferBuilder* builder, |
4216 | 0 | const Parser& parser) const { |
4217 | 0 | std::vector<Offset<reflection::EnumVal>> enumval_offsets; |
4218 | 0 | for (auto it = vals.vec.begin(); it != vals.vec.end(); ++it) { |
4219 | 0 | enumval_offsets.push_back((*it)->Serialize(builder, parser)); |
4220 | 0 | } |
4221 | 0 | const auto qualified_name = defined_namespace->GetFullyQualifiedName(name); |
4222 | 0 | const auto name__ = builder->CreateString(qualified_name); |
4223 | 0 | const auto vals__ = builder->CreateVector(enumval_offsets); |
4224 | 0 | const auto type__ = underlying_type.Serialize(builder); |
4225 | 0 | const auto attr__ = SerializeAttributes(builder, parser); |
4226 | 0 | const auto docs__ = parser.opts.binary_schema_comments && !doc_comment.empty() |
4227 | 0 | ? builder->CreateVectorOfStrings(doc_comment) |
4228 | 0 | : 0; |
4229 | 0 | std::string decl_file_in_project = declaration_file ? *declaration_file : ""; |
4230 | 0 | const auto file__ = builder->CreateSharedString(decl_file_in_project); |
4231 | 0 | return reflection::CreateEnum(*builder, name__, vals__, is_union, type__, |
4232 | 0 | attr__, docs__, file__); |
4233 | 0 | } |
4234 | | |
4235 | 14 | bool EnumDef::Deserialize(Parser& parser, const reflection::Enum* _enum) { |
4236 | 14 | name = parser.UnqualifiedName(_enum->name()->str()); |
4237 | 60 | for (uoffset_t i = 0; i < _enum->values()->size(); ++i) { |
4238 | 46 | auto val = new EnumVal(); |
4239 | 46 | if (!val->Deserialize(parser, _enum->values()->Get(i)) || |
4240 | 46 | vals.Add(val->name, val)) { |
4241 | 0 | delete val; |
4242 | 0 | return false; |
4243 | 0 | } |
4244 | 46 | } |
4245 | 14 | is_union = _enum->is_union(); |
4246 | 14 | if (!underlying_type.Deserialize(parser, _enum->underlying_type())) { |
4247 | 0 | return false; |
4248 | 0 | } |
4249 | 14 | if (!DeserializeAttributes(parser, _enum->attributes())) return false; |
4250 | 14 | DeserializeDoc(doc_comment, _enum->documentation()); |
4251 | 14 | return true; |
4252 | 14 | } |
4253 | | |
4254 | | flatbuffers::Offset< |
4255 | | flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>> |
4256 | | EnumVal::SerializeAttributes(FlatBufferBuilder* builder, |
4257 | 0 | const Parser& parser) const { |
4258 | 0 | return SerializeAttributesCommon(attributes, builder, parser); |
4259 | 0 | } |
4260 | | |
4261 | | bool EnumVal::DeserializeAttributes( |
4262 | 46 | Parser& parser, const Vector<Offset<reflection::KeyValue>>* attrs) { |
4263 | 46 | return DeserializeAttributesCommon(attributes, parser, attrs); |
4264 | 46 | } |
4265 | | |
4266 | | Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder* builder, |
4267 | 0 | const Parser& parser) const { |
4268 | 0 | const auto name__ = builder->CreateString(name); |
4269 | 0 | const auto type__ = union_type.Serialize(builder); |
4270 | 0 | const auto attr__ = SerializeAttributes(builder, parser); |
4271 | 0 | const auto docs__ = parser.opts.binary_schema_comments && !doc_comment.empty() |
4272 | 0 | ? builder->CreateVectorOfStrings(doc_comment) |
4273 | 0 | : 0; |
4274 | 0 | return reflection::CreateEnumVal(*builder, name__, value, type__, docs__, |
4275 | 0 | attr__); |
4276 | 0 | } |
4277 | | |
4278 | 46 | bool EnumVal::Deserialize(Parser& parser, const reflection::EnumVal* val) { |
4279 | 46 | name = val->name()->str(); |
4280 | 46 | value = val->value(); |
4281 | 46 | if (!union_type.Deserialize(parser, val->union_type())) return false; |
4282 | 46 | if (!DeserializeAttributes(parser, val->attributes())) return false; |
4283 | 46 | DeserializeDoc(doc_comment, val->documentation()); |
4284 | 46 | return true; |
4285 | 46 | } |
4286 | | |
4287 | 0 | Offset<reflection::Type> Type::Serialize(FlatBufferBuilder* builder) const { |
4288 | 0 | size_t element_size = SizeOf(element); |
4289 | 0 | if (base_type == BASE_TYPE_VECTOR && element == BASE_TYPE_STRUCT && |
4290 | 0 | struct_def->bytesize != 0) { |
4291 | | // struct_def->bytesize==0 means struct is table |
4292 | 0 | element_size = struct_def->bytesize; |
4293 | 0 | } |
4294 | 0 | return reflection::CreateType( |
4295 | 0 | *builder, static_cast<reflection::BaseType>(base_type), |
4296 | 0 | static_cast<reflection::BaseType>(element), |
4297 | 0 | struct_def ? struct_def->index : (enum_def ? enum_def->index : -1), |
4298 | 0 | fixed_length, static_cast<uint32_t>(SizeOf(base_type)), |
4299 | 0 | static_cast<uint32_t>(element_size)); |
4300 | 0 | } |
4301 | | |
4302 | 252 | bool Type::Deserialize(const Parser& parser, const reflection::Type* type) { |
4303 | 252 | if (type == nullptr) return true; |
4304 | 252 | base_type = static_cast<BaseType>(type->base_type()); |
4305 | 252 | element = static_cast<BaseType>(type->element()); |
4306 | 252 | fixed_length = type->fixed_length(); |
4307 | 252 | if (type->index() >= 0) { |
4308 | 96 | bool is_series = type->base_type() == reflection::Vector || |
4309 | 80 | type->base_type() == reflection::Array; |
4310 | 96 | if (type->base_type() == reflection::Obj || |
4311 | 56 | (is_series && type->element() == reflection::Obj)) { |
4312 | 56 | if (static_cast<size_t>(type->index()) < parser.structs_.vec.size()) { |
4313 | 56 | struct_def = parser.structs_.vec[type->index()]; |
4314 | 56 | struct_def->refcount++; |
4315 | 56 | } else { |
4316 | 0 | return false; |
4317 | 0 | } |
4318 | 56 | } else { |
4319 | 40 | if (static_cast<size_t>(type->index()) < parser.enums_.vec.size()) { |
4320 | 40 | enum_def = parser.enums_.vec[type->index()]; |
4321 | 40 | } else { |
4322 | 0 | return false; |
4323 | 0 | } |
4324 | 40 | } |
4325 | 96 | } |
4326 | 252 | return true; |
4327 | 252 | } |
4328 | | |
4329 | | flatbuffers::Offset< |
4330 | | flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>> |
4331 | | Definition::SerializeAttributes(FlatBufferBuilder* builder, |
4332 | 0 | const Parser& parser) const { |
4333 | 0 | return SerializeAttributesCommon(attributes, builder, parser); |
4334 | 0 | } |
4335 | | |
4336 | | bool Definition::DeserializeAttributes( |
4337 | 246 | Parser& parser, const Vector<Offset<reflection::KeyValue>>* attrs) { |
4338 | 246 | return DeserializeAttributesCommon(attributes, parser, attrs); |
4339 | 246 | } |
4340 | | |
4341 | | /************************************************************************/ |
4342 | | /* DESERIALIZATION */ |
4343 | | /************************************************************************/ |
4344 | 2 | bool Parser::Deserialize(const uint8_t* buf, const size_t size) { |
4345 | 2 | flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t*>(buf), size); |
4346 | 2 | bool size_prefixed = false; |
4347 | 2 | if (!reflection::SchemaBufferHasIdentifier(buf)) { |
4348 | 0 | if (!flatbuffers::BufferHasIdentifier(buf, reflection::SchemaIdentifier(), |
4349 | 0 | true)) |
4350 | 0 | return false; |
4351 | 0 | else |
4352 | 0 | size_prefixed = true; |
4353 | 0 | } |
4354 | 2 | auto verify_fn = size_prefixed ? &reflection::VerifySizePrefixedSchemaBuffer |
4355 | 2 | : &reflection::VerifySchemaBuffer; |
4356 | 2 | if (!verify_fn(verifier)) { |
4357 | 0 | return false; |
4358 | 0 | } |
4359 | 2 | auto schema = size_prefixed ? reflection::GetSizePrefixedSchema(buf) |
4360 | 2 | : reflection::GetSchema(buf); |
4361 | 2 | return Deserialize(schema); |
4362 | 2 | } |
4363 | | |
4364 | 2 | bool Parser::Deserialize(const reflection::Schema* schema) { |
4365 | 2 | file_identifier_ = schema->file_ident() ? schema->file_ident()->str() : ""; |
4366 | 2 | file_extension_ = schema->file_ext() ? schema->file_ext()->str() : ""; |
4367 | 2 | std::map<std::string, Namespace*> namespaces_index; |
4368 | | |
4369 | | // Create defs without deserializing so references from fields to structs and |
4370 | | // enums can be resolved. |
4371 | 32 | for (auto it = schema->objects()->begin(); it != schema->objects()->end(); |
4372 | 30 | ++it) { |
4373 | 30 | auto struct_def = new StructDef(); |
4374 | 30 | struct_def->bytesize = it->bytesize(); |
4375 | 30 | struct_def->fixed = it->is_struct(); |
4376 | 30 | struct_def->minalign = it->minalign(); |
4377 | 30 | if (structs_.Add(it->name()->str(), struct_def)) { |
4378 | 0 | delete struct_def; |
4379 | 0 | return false; |
4380 | 0 | } |
4381 | 30 | auto type = new Type(BASE_TYPE_STRUCT, struct_def, nullptr); |
4382 | 30 | if (types_.Add(it->name()->str(), type)) { |
4383 | 0 | delete type; |
4384 | 0 | return false; |
4385 | 0 | } |
4386 | 30 | } |
4387 | 16 | for (auto it = schema->enums()->begin(); it != schema->enums()->end(); ++it) { |
4388 | 14 | auto enum_def = new EnumDef(); |
4389 | 14 | if (enums_.Add(it->name()->str(), enum_def)) { |
4390 | 0 | delete enum_def; |
4391 | 0 | return false; |
4392 | 0 | } |
4393 | 14 | auto type = new Type(BASE_TYPE_UNION, nullptr, enum_def); |
4394 | 14 | if (types_.Add(it->name()->str(), type)) { |
4395 | 0 | delete type; |
4396 | 0 | return false; |
4397 | 0 | } |
4398 | 14 | } |
4399 | | |
4400 | | // Now fields can refer to structs and enums by index. |
4401 | 32 | for (auto it = schema->objects()->begin(); it != schema->objects()->end(); |
4402 | 30 | ++it) { |
4403 | 30 | std::string qualified_name = it->name()->str(); |
4404 | 30 | auto struct_def = structs_.Lookup(qualified_name); |
4405 | 30 | struct_def->defined_namespace = |
4406 | 30 | GetNamespace(qualified_name, namespaces_, namespaces_index); |
4407 | 30 | if (!struct_def->Deserialize(*this, *it)) { |
4408 | 0 | return false; |
4409 | 0 | } |
4410 | 30 | if (schema->root_table() == *it) { |
4411 | 2 | root_struct_def_ = struct_def; |
4412 | 2 | } |
4413 | 30 | } |
4414 | 16 | for (auto it = schema->enums()->begin(); it != schema->enums()->end(); ++it) { |
4415 | 14 | std::string qualified_name = it->name()->str(); |
4416 | 14 | auto enum_def = enums_.Lookup(qualified_name); |
4417 | 14 | enum_def->defined_namespace = |
4418 | 14 | GetNamespace(qualified_name, namespaces_, namespaces_index); |
4419 | 14 | if (!enum_def->Deserialize(*this, *it)) { |
4420 | 0 | return false; |
4421 | 0 | } |
4422 | 14 | } |
4423 | | |
4424 | 2 | if (schema->services()) { |
4425 | 4 | for (auto it = schema->services()->begin(); it != schema->services()->end(); |
4426 | 2 | ++it) { |
4427 | 2 | std::string qualified_name = it->name()->str(); |
4428 | 2 | auto service_def = new ServiceDef(); |
4429 | 2 | service_def->defined_namespace = |
4430 | 2 | GetNamespace(qualified_name, namespaces_, namespaces_index); |
4431 | 2 | if (!service_def->Deserialize(*this, *it) || |
4432 | 2 | services_.Add(qualified_name, service_def)) { |
4433 | 0 | delete service_def; |
4434 | 0 | return false; |
4435 | 0 | } |
4436 | 2 | } |
4437 | 2 | } |
4438 | 2 | advanced_features_ = schema->advanced_features(); |
4439 | | |
4440 | 2 | if (schema->fbs_files()) |
4441 | 8 | for (auto s = schema->fbs_files()->begin(); s != schema->fbs_files()->end(); |
4442 | 6 | ++s) { |
4443 | 6 | for (auto f = s->included_filenames()->begin(); |
4444 | 16 | f != s->included_filenames()->end(); ++f) { |
4445 | 10 | IncludedFile included_file; |
4446 | 10 | included_file.filename = f->str(); |
4447 | 10 | files_included_per_file_[s->filename()->str()].insert(included_file); |
4448 | 10 | } |
4449 | 6 | } |
4450 | | |
4451 | 2 | return true; |
4452 | 2 | } |
4453 | | |
4454 | 0 | std::string Parser::ConformTo(const Parser& base) { |
4455 | 0 | for (auto sit = structs_.vec.begin(); sit != structs_.vec.end(); ++sit) { |
4456 | 0 | auto& struct_def = **sit; |
4457 | 0 | auto qualified_name = |
4458 | 0 | struct_def.defined_namespace->GetFullyQualifiedName(struct_def.name); |
4459 | 0 | auto struct_def_base = base.LookupStruct(qualified_name); |
4460 | 0 | if (!struct_def_base) continue; |
4461 | 0 | std::set<FieldDef*> renamed_fields; |
4462 | 0 | for (auto fit = struct_def.fields.vec.begin(); |
4463 | 0 | fit != struct_def.fields.vec.end(); ++fit) { |
4464 | 0 | auto& field = **fit; |
4465 | 0 | auto field_base = struct_def_base->fields.Lookup(field.name); |
4466 | 0 | const auto qualified_field_name = qualified_name + "." + field.name; |
4467 | 0 | if (field_base) { |
4468 | 0 | if (field.value.offset != field_base->value.offset) { |
4469 | 0 | return "offsets differ for field: " + qualified_field_name; |
4470 | 0 | } |
4471 | 0 | if (field.value.constant != field_base->value.constant) { |
4472 | 0 | return "defaults differ for field: " + qualified_field_name; |
4473 | 0 | } |
4474 | 0 | if (!EqualByName(field.value.type, field_base->value.type)) { |
4475 | 0 | return "types differ for field: " + qualified_field_name; |
4476 | 0 | } |
4477 | 0 | if (field.offset64 != field_base->offset64) { |
4478 | 0 | return "offset types differ for field: " + qualified_field_name; |
4479 | 0 | } |
4480 | 0 | } else { |
4481 | | // Doesn't have to exist, deleting fields is fine. |
4482 | | // But we should check if there is a field that has the same offset |
4483 | | // but is incompatible (in the case of field renaming). |
4484 | 0 | for (auto fbit = struct_def_base->fields.vec.begin(); |
4485 | 0 | fbit != struct_def_base->fields.vec.end(); ++fbit) { |
4486 | 0 | field_base = *fbit; |
4487 | 0 | if (field.value.offset == field_base->value.offset) { |
4488 | 0 | renamed_fields.insert(field_base); |
4489 | 0 | if (!EqualByName(field.value.type, field_base->value.type)) { |
4490 | 0 | const auto qualified_field_base = |
4491 | 0 | qualified_name + "." + field_base->name; |
4492 | 0 | return "field renamed to different type: " + |
4493 | 0 | qualified_field_name + " (renamed from " + |
4494 | 0 | qualified_field_base + ")"; |
4495 | 0 | } |
4496 | 0 | break; |
4497 | 0 | } |
4498 | 0 | } |
4499 | 0 | } |
4500 | 0 | } |
4501 | | // deletion of trailing fields are not allowed |
4502 | 0 | for (auto fit = struct_def_base->fields.vec.begin(); |
4503 | 0 | fit != struct_def_base->fields.vec.end(); ++fit) { |
4504 | 0 | auto& field_base = **fit; |
4505 | | // not a renamed field |
4506 | 0 | if (renamed_fields.find(&field_base) == renamed_fields.end()) { |
4507 | 0 | auto field = struct_def.fields.Lookup(field_base.name); |
4508 | 0 | if (!field) { |
4509 | 0 | return "field deleted: " + qualified_name + "." + field_base.name; |
4510 | 0 | } |
4511 | 0 | } |
4512 | 0 | } |
4513 | 0 | } |
4514 | | |
4515 | 0 | for (auto eit = enums_.vec.begin(); eit != enums_.vec.end(); ++eit) { |
4516 | 0 | auto& enum_def = **eit; |
4517 | 0 | auto qualified_name = |
4518 | 0 | enum_def.defined_namespace->GetFullyQualifiedName(enum_def.name); |
4519 | 0 | auto enum_def_base = base.enums_.Lookup(qualified_name); |
4520 | 0 | if (!enum_def_base) continue; |
4521 | 0 | for (auto evit = enum_def.Vals().begin(); evit != enum_def.Vals().end(); |
4522 | 0 | ++evit) { |
4523 | 0 | auto& enum_val = **evit; |
4524 | 0 | auto enum_val_base = enum_def_base->Lookup(enum_val.name); |
4525 | 0 | if (enum_val_base) { |
4526 | 0 | if (enum_val != *enum_val_base) |
4527 | 0 | return "values differ for enum: " + enum_val.name; |
4528 | 0 | } |
4529 | 0 | } |
4530 | | // Check underlying type changes |
4531 | 0 | if (enum_def_base->underlying_type.base_type != |
4532 | 0 | enum_def.underlying_type.base_type) { |
4533 | 0 | return "underlying type differ for " + |
4534 | 0 | std::string(enum_def.is_union ? "union: " : "enum: ") + |
4535 | 0 | qualified_name; |
4536 | 0 | } |
4537 | 0 | } |
4538 | 0 | return ""; |
4539 | 0 | } |
4540 | | |
4541 | | } // namespace flatbuffers |