Line data Source code
1 : /***************************************************************************\
2 : * Name : ast render *
3 : * Description : resolve types in ast tree *
4 : * Author : antonin.kriz@gmail.com *
5 : * ------------------------------------------------------------------------- *
6 : * This is free software; you can redistribute it and/or modify it under the *
7 : * terms of the MIT license. A copy of the license can be found in the file *
8 : * "LICENSE" at the root of this distribution. *
9 : \***************************************************************************/
10 :
11 : #include "ast/proto-oneof.h"
12 :
13 : #include "../dumper/header.h"
14 : #include "../parser/options.h"
15 : #include "proto-field.h"
16 : #include "proto-file.h"
17 : #include "proto-message.h"
18 : #include "spb/pb/wire-types.h"
19 : #include <algorithm>
20 : #include <array>
21 : #include <string_view>
22 : #include <utility>
23 :
24 : namespace
25 : {
26 : using namespace std::literals;
27 :
28 : struct search_ctx
29 : {
30 : //- `file` containing `message`
31 : const proto_file & file;
32 : //- `message` containing `field`
33 : const proto_message & message;
34 : //- parent message (null for top level)
35 : const search_ctx * p_parent = nullptr;
36 : };
37 :
38 : using scalar_encoder = spb::pb::detail::scalar_encoder;
39 :
40 1391 : [[nodiscard]] auto type_parts( std::string_view type_name ) -> size_t
41 : {
42 1391 : return size_t( std::count( type_name.cbegin( ), type_name.cend( ), '.' ) );
43 : }
44 :
45 1389 : [[nodiscard]] auto is_last_part( const proto_field & field, size_t type_part ) -> bool
46 : {
47 1389 : return type_part == type_parts( field.type_name );
48 : }
49 :
50 1744 : [[nodiscard]] auto get_type_part( const proto_field & field, size_t type_part ) -> std::string_view
51 : {
52 1744 : auto type_name = field.type_name;
53 1821 : for( ; type_part > 0; type_part-- )
54 : {
55 77 : const auto dot_index = type_name.find( '.' );
56 77 : if( dot_index == type_name.npos )
57 0 : return { };
58 :
59 77 : type_name.remove_prefix( dot_index + 1 );
60 : }
61 :
62 1744 : return type_name.substr( 0, type_name.find( '.' ) );
63 : }
64 :
65 : [[nodiscard]] auto resolve_type( const search_ctx & self, const proto_field & field,
66 : size_t type_part )
67 : -> std::pair< proto_field::Type, proto_field::BitType >;
68 :
69 114 : [[nodiscard]] auto remove_bitfield( std::string_view type ) -> std::string_view
70 : {
71 114 : return type.substr( 0, type.find( ':' ) );
72 : }
73 :
74 114 : [[nodiscard]] auto convertible_types( proto_field::Type from, proto_field::BitType to ) -> bool
75 : {
76 : static constexpr auto type_map =
77 : std::array< std::pair< proto_field::Type, std::array< proto_field::BitType, 4 > >, 15 >{ {
78 : { proto_field::Type::INT32,
79 : { { proto_field::BitType::INT8, proto_field::BitType::INT16,
80 : proto_field::BitType::INT32 } } },
81 : { proto_field::Type::INT64,
82 : { { proto_field::BitType::INT8, proto_field::BitType::INT16,
83 : proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
84 : { proto_field::Type::SINT32,
85 : { { proto_field::BitType::INT8, proto_field::BitType::INT16,
86 : proto_field::BitType::INT32 } } },
87 : { proto_field::Type::SINT64,
88 : { { proto_field::BitType::INT8, proto_field::BitType::INT16,
89 : proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
90 : { proto_field::Type::UINT32,
91 : { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
92 : proto_field::BitType::UINT32 } } },
93 : { proto_field::Type::UINT64,
94 : { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
95 : proto_field::BitType::UINT32, proto_field::BitType::UINT64 } } },
96 : { proto_field::Type::FIXED32,
97 : { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
98 : proto_field::BitType::UINT32 } } },
99 : { proto_field::Type::FIXED64,
100 : { { proto_field::BitType::UINT8, proto_field::BitType::UINT16,
101 : proto_field::BitType::UINT32, proto_field::BitType::UINT64 } } },
102 : { proto_field::Type::SFIXED32,
103 : { { proto_field::BitType::INT8, proto_field::BitType::INT16,
104 : proto_field::BitType::INT32 } } },
105 : { proto_field::Type::SFIXED64,
106 : { { proto_field::BitType::INT8, proto_field::BitType::INT16,
107 : proto_field::BitType::INT32, proto_field::BitType::INT64 } } },
108 : } };
109 :
110 722 : for( auto [ proto_type, types ] : type_map )
111 : {
112 722 : if( from == proto_type )
113 114 : return std::find( types.begin( ), types.end( ), to ) != types.end( );
114 : }
115 0 : return false;
116 : }
117 :
118 114 : [[nodiscard]] auto get_scalar_bit_type( std::string_view type_name ) -> proto_field::BitType
119 : {
120 : struct type_table
121 : {
122 : std::string_view name;
123 : proto_field::BitType type;
124 : };
125 :
126 : static constexpr auto scalar_types = std::array< type_table, 8 >{ {
127 : { "int8"sv, proto_field::BitType::INT8 },
128 : { "int16"sv, proto_field::BitType::INT16 },
129 : { "int32"sv, proto_field::BitType::INT32 },
130 : { "int64"sv, proto_field::BitType::INT64 },
131 : { "uint8"sv, proto_field::BitType::UINT8 },
132 : { "uint16"sv, proto_field::BitType::UINT16 },
133 : { "uint32"sv, proto_field::BitType::UINT32 },
134 : { "uint64"sv, proto_field::BitType::UINT64 },
135 : } };
136 :
137 416 : for( const auto item : scalar_types )
138 : {
139 416 : if( item.name == type_name )
140 114 : return item.type;
141 : }
142 :
143 0 : return proto_field::BitType::NONE;
144 : }
145 :
146 1769 : [[nodiscard]] auto get_scalar_proto_type( std::string_view type_name ) -> proto_field::Type
147 : {
148 : struct type_table
149 : {
150 : std::string_view name;
151 : proto_field::Type type;
152 : };
153 :
154 : static constexpr auto scalar_types = std::array< type_table, 15 >{ {
155 : { "bool"sv, proto_field::Type::BOOL },
156 : { "bytes"sv, proto_field::Type::BYTES },
157 : { "double"sv, proto_field::Type::DOUBLE },
158 : { "float"sv, proto_field::Type::FLOAT },
159 : { "int32"sv, proto_field::Type::INT32 },
160 : { "int64"sv, proto_field::Type::INT64 },
161 : { "uint32"sv, proto_field::Type::UINT32 },
162 : { "uint64"sv, proto_field::Type::UINT64 },
163 : { "sint32"sv, proto_field::Type::SINT32 },
164 : { "sint64"sv, proto_field::Type::SINT64 },
165 : { "fixed32"sv, proto_field::Type::FIXED32 },
166 : { "fixed64"sv, proto_field::Type::FIXED64 },
167 : { "sfixed32"sv, proto_field::Type::SFIXED32 },
168 : { "sfixed64"sv, proto_field::Type::SFIXED64 },
169 : { "string"sv, proto_field::Type::STRING },
170 : } };
171 :
172 22466 : for( const auto item : scalar_types )
173 : {
174 21522 : if( item.name == type_name )
175 825 : return item.type;
176 : }
177 :
178 944 : return proto_field::Type::NONE;
179 : }
180 :
181 1769 : [[nodiscard]] auto get_field_type( const proto_file & file, const proto_field & field )
182 : -> std::pair< proto_field::Type, proto_field::BitType >
183 : {
184 1769 : const auto type = get_scalar_proto_type( field.type_name );
185 1769 : if( type == proto_field::Type::NONE )
186 944 : return { };
187 :
188 825 : if( const auto p_name = field.options.find( option_field_type );
189 825 : p_name != field.options.end( ) )
190 : {
191 114 : const auto field_type = get_scalar_bit_type( remove_bitfield( p_name->second ) );
192 114 : if( !convertible_types( type, field_type ) )
193 : {
194 0 : throw_parse_error( file, p_name->second,
195 0 : std::string( "incompatible int type: " ) +
196 0 : std::string( field.type_name ) + " and " +
197 0 : std::string( p_name->second ) );
198 : }
199 114 : return { type, field_type };
200 : }
201 :
202 711 : return { type, proto_field::BitType::NONE };
203 : }
204 :
205 1769 : [[nodiscard]] auto get_scalar_type( const proto_file & file, const proto_field & field,
206 : size_t type_part )
207 : -> std::pair< proto_field::Type, proto_field::BitType >
208 : {
209 1769 : if( type_part != 0 )
210 0 : return { };
211 :
212 1769 : return get_field_type( file, field );
213 : }
214 :
215 850 : [[nodiscard]] auto get_sub_message( const proto_message & message, const proto_field & field,
216 : size_t type_part ) -> const proto_message *
217 : {
218 850 : const auto type_name = get_type_part( field, type_part );
219 850 : const auto index = std::find_if( message.messages.begin( ), message.messages.end( ),
220 8727 : [ type_name ]( const auto & sub_message ) -> bool
221 8727 : { return type_name == sub_message.name; } );
222 850 : return ( index != message.messages.end( ) ) ? &*index : nullptr;
223 : }
224 :
225 983 : [[nodiscard]] auto resolve_enum( const proto_message & message, const proto_field & field,
226 : size_t type_part ) -> proto_field::Type
227 : {
228 983 : if( !is_last_part( field, type_part ) )
229 89 : return proto_field::Type::NONE;
230 :
231 894 : const auto type_name = get_type_part( field, type_part );
232 :
233 894 : return std::any_of( message.enums.begin( ), message.enums.end( ),
234 1122 : [ type_name ]( const auto & enum_ ) -> bool
235 1122 : { return type_name == enum_.name; } )
236 894 : ? proto_field::Type::ENUM
237 894 : : proto_field::Type::NONE;
238 : }
239 :
240 983 : [[nodiscard]] auto resolve_from_message( const proto_message & message, const proto_field & field,
241 : size_t type_part ) -> proto_field::Type
242 : {
243 983 : if( const auto type = resolve_enum( message, field, type_part ); type )
244 133 : return type;
245 :
246 850 : if( const auto * sub_message = get_sub_message( message, field, type_part ); sub_message )
247 : {
248 406 : if( is_last_part( field, type_part ) )
249 369 : return proto_field::Type::MESSAGE;
250 :
251 37 : return resolve_from_message( *sub_message, field, type_part + 1 );
252 : }
253 :
254 444 : return proto_field::Type::NONE;
255 : }
256 :
257 444 : [[nodiscard]] auto resolve_from_parent( const search_ctx & self, const proto_field & field,
258 : size_t type_part ) -> proto_field::Type
259 : {
260 444 : if( !self.p_parent || type_part > 0 )
261 2 : return proto_field::Type::NONE;
262 :
263 442 : return resolve_type( *self.p_parent, field, type_part ).first;
264 : }
265 :
266 2 : [[nodiscard]] auto resolve_from_import( const proto_file & import, const proto_field & field )
267 : -> proto_field::Type
268 : {
269 2 : if( import.package.name.empty( ) )
270 : {
271 0 : return resolve_from_message( import.package, field, 0 );
272 : }
273 :
274 2 : if( field.type_name.size( ) > import.package.name.size( ) &&
275 4 : field.type_name[ import.package.name.size( ) ] == '.' &&
276 2 : field.type_name.starts_with( import.package.name ) )
277 : {
278 2 : return resolve_from_message( import.package, field, type_parts( import.package.name ) + 1 );
279 : }
280 :
281 0 : return proto_field::Type::NONE;
282 : }
283 :
284 2 : [[nodiscard]] auto resolve_from_imports( const search_ctx & self, const proto_field & field,
285 : size_t type_part ) -> proto_field::Type
286 : {
287 2 : if( type_part > 0 )
288 0 : return proto_field::Type::NONE;
289 :
290 2 : for( const auto & import : self.file.file_imports )
291 : {
292 2 : if( const auto type = resolve_from_import( import, field ); type )
293 2 : return type;
294 : }
295 :
296 0 : return proto_field::Type::NONE;
297 : }
298 :
299 1769 : auto resolve_type( const search_ctx & self, const proto_field & field, size_t type_part )
300 : -> std::pair< proto_field::Type, proto_field::BitType >
301 : {
302 1769 : if( const auto type = get_scalar_type( self.file, field, type_part ); type.first )
303 825 : return type;
304 :
305 944 : if( const auto type = resolve_from_message( self.message, field, type_part ); type )
306 500 : return { type, proto_field::BitType::NONE };
307 :
308 444 : if( const auto type = resolve_from_parent( self, field, type_part ); type )
309 442 : return { type, proto_field::BitType::NONE };
310 :
311 2 : if( const auto type = resolve_from_imports( self, field, type_part ); type )
312 2 : return { type, proto_field::BitType::NONE };
313 :
314 0 : throw_parse_error( self.file, field.type_name, "type can't be resolved" );
315 : }
316 :
317 1327 : void resolve_types( const search_ctx & self, proto_field & field )
318 : {
319 1327 : const auto [ type, bit_type ] = resolve_type( self, field, 0 );
320 :
321 1327 : field.type = type;
322 1327 : field.bit_type = bit_type;
323 1327 : }
324 :
325 52 : void resolve_types( const search_ctx & self, proto_map & map )
326 : {
327 52 : resolve_types( self, map.key );
328 52 : resolve_types( self, map.value );
329 52 : }
330 :
331 4 : void resolve_types( const search_ctx & self, proto_oneof & oneof )
332 : {
333 15 : for( auto & field : oneof.fields )
334 : {
335 11 : resolve_types( self, field );
336 : }
337 4 : }
338 :
339 466 : void resolve_types( const search_ctx & parent, proto_message & message )
340 : {
341 : auto ctx = search_ctx{
342 466 : .file = parent.file,
343 : .message = message,
344 : .p_parent = &parent,
345 466 : };
346 :
347 1678 : for( auto & field : message.fields )
348 : {
349 1212 : resolve_types( ctx, field );
350 : }
351 :
352 518 : for( auto & map : message.maps )
353 : {
354 52 : resolve_types( ctx, map );
355 : }
356 :
357 470 : for( auto & oneof : message.oneofs )
358 : {
359 4 : resolve_types( ctx, oneof );
360 : }
361 :
362 918 : for( auto & sub_message : message.messages )
363 : {
364 452 : resolve_types( ctx, sub_message );
365 : }
366 466 : }
367 : }// namespace
368 :
369 1448 : auto is_packed_array( const proto_file & file, const proto_field & field ) -> bool
370 : {
371 1448 : if( field.label != proto_field::Label::REPEATED )
372 1210 : return { };
373 :
374 238 : if( file.syntax.version < 3 )
375 : {
376 150 : const auto p_packed = field.options.find( "packed" );
377 150 : return p_packed != field.options.end( ) && p_packed->second == "true";
378 : }
379 : else
380 : {
381 88 : const auto p_packed = field.options.find( "packed" );
382 88 : return p_packed == field.options.end( ) || p_packed->second != "false";
383 : }
384 : }
385 :
386 2569 : auto is_scalar( const proto_field::Type & type ) -> bool
387 : {
388 2569 : switch( type )
389 : {
390 1552 : case proto_field::Type::NONE:
391 : case proto_field::Type::MESSAGE:
392 : case proto_field::Type::ENUM:
393 1552 : return false;
394 :
395 1017 : case proto_field::Type::BYTES:
396 : case proto_field::Type::STRING:
397 : case proto_field::Type::BOOL:
398 : case proto_field::Type::INT32:
399 : case proto_field::Type::UINT32:
400 : case proto_field::Type::INT64:
401 : case proto_field::Type::UINT64:
402 : case proto_field::Type::SINT32:
403 : case proto_field::Type::SINT64:
404 : case proto_field::Type::FLOAT:
405 : case proto_field::Type::FIXED32:
406 : case proto_field::Type::SFIXED32:
407 : case proto_field::Type::DOUBLE:
408 : case proto_field::Type::FIXED64:
409 : case proto_field::Type::SFIXED64:
410 1017 : return true;
411 : }
412 :
413 0 : return false;
414 : }
415 :
416 14 : void resolve_types( proto_file & file )
417 : {
418 14 : auto ctx = search_ctx{
419 : .file = file,
420 14 : .message = file.package,
421 : .p_parent = nullptr,
422 14 : };
423 14 : resolve_types( ctx, file.package );
424 14 : }
|