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 <algorithm>
17 : #include "proto-file.h"
18 : #include "proto-message.h"
19 : #include "spb/pb/wire-types.h"
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( field.type_name.size( ) > import.package.name.size( ) &&
270 4 : field.type_name[ import.package.name.size( ) ] == '.' &&
271 2 : field.type_name.starts_with( import.package.name ) )
272 : {
273 2 : return resolve_from_message( import.package, field, type_parts( import.package.name ) + 1 );
274 : }
275 :
276 0 : return proto_field::Type::NONE;
277 : }
278 :
279 2 : [[nodiscard]] auto resolve_from_imports( const search_ctx & self, const proto_field & field,
280 : size_t type_part ) -> proto_field::Type
281 : {
282 2 : if( type_part > 0 )
283 0 : return proto_field::Type::NONE;
284 :
285 2 : for( const auto & import : self.file.file_imports )
286 : {
287 2 : if( const auto type = resolve_from_import( import, field ); type )
288 2 : return type;
289 : }
290 :
291 0 : return proto_field::Type::NONE;
292 : }
293 :
294 1769 : auto resolve_type( const search_ctx & self, const proto_field & field, size_t type_part )
295 : -> std::pair< proto_field::Type, proto_field::BitType >
296 : {
297 1769 : if( const auto type = get_scalar_type( self.file, field, type_part ); type.first )
298 825 : return type;
299 :
300 944 : if( const auto type = resolve_from_message( self.message, field, type_part ); type )
301 500 : return { type, proto_field::BitType::NONE };
302 :
303 444 : if( const auto type = resolve_from_parent( self, field, type_part ); type )
304 442 : return { type, proto_field::BitType::NONE };
305 :
306 2 : if( const auto type = resolve_from_imports( self, field, type_part ); type )
307 2 : return { type, proto_field::BitType::NONE };
308 :
309 0 : throw_parse_error( self.file, field.type_name, "type can't be resolved" );
310 : }
311 :
312 1327 : void resolve_types( const search_ctx & self, proto_field & field )
313 : {
314 1327 : const auto [ type, bit_type ] = resolve_type( self, field, 0 );
315 :
316 1327 : field.type = type;
317 1327 : field.bit_type = bit_type;
318 1327 : }
319 :
320 52 : void resolve_types( const search_ctx & self, proto_map & map )
321 : {
322 52 : resolve_types( self, map.key );
323 52 : resolve_types( self, map.value );
324 52 : }
325 :
326 4 : void resolve_types( const search_ctx & self, proto_oneof & oneof )
327 : {
328 15 : for( auto & field : oneof.fields )
329 : {
330 11 : resolve_types( self, field );
331 : }
332 4 : }
333 :
334 466 : void resolve_types( const search_ctx & parent, proto_message & message )
335 : {
336 : auto ctx = search_ctx{
337 466 : .file = parent.file,
338 : .message = message,
339 : .p_parent = &parent,
340 466 : };
341 :
342 1678 : for( auto & field : message.fields )
343 : {
344 1212 : resolve_types( ctx, field );
345 : }
346 :
347 518 : for( auto & map : message.maps )
348 : {
349 52 : resolve_types( ctx, map );
350 : }
351 :
352 470 : for( auto & oneof : message.oneofs )
353 : {
354 4 : resolve_types( ctx, oneof );
355 : }
356 :
357 918 : for( auto & sub_message : message.messages )
358 : {
359 452 : resolve_types( ctx, sub_message );
360 : }
361 466 : }
362 : }// namespace
363 :
364 1448 : auto is_packed_array( const proto_file & file, const proto_field & field ) -> bool
365 : {
366 1448 : if( field.label != proto_field::Label::REPEATED )
367 1210 : return { };
368 :
369 238 : if( file.syntax.version < 3 )
370 : {
371 150 : const auto p_packed = field.options.find( "packed" );
372 150 : return p_packed != field.options.end( ) && p_packed->second == "true";
373 : }
374 : else
375 : {
376 88 : const auto p_packed = field.options.find( "packed" );
377 88 : return p_packed == field.options.end( ) || p_packed->second != "false";
378 : }
379 : }
380 :
381 1936 : auto is_scalar( const proto_field::Type & type ) -> bool
382 : {
383 1936 : switch( type )
384 : {
385 906 : case proto_field::Type::NONE:
386 : case proto_field::Type::MESSAGE:
387 : case proto_field::Type::ENUM:
388 906 : return false;
389 :
390 1030 : case proto_field::Type::BYTES:
391 : case proto_field::Type::STRING:
392 : case proto_field::Type::BOOL:
393 : case proto_field::Type::INT32:
394 : case proto_field::Type::UINT32:
395 : case proto_field::Type::INT64:
396 : case proto_field::Type::UINT64:
397 : case proto_field::Type::SINT32:
398 : case proto_field::Type::SINT64:
399 : case proto_field::Type::FLOAT:
400 : case proto_field::Type::FIXED32:
401 : case proto_field::Type::SFIXED32:
402 : case proto_field::Type::DOUBLE:
403 : case proto_field::Type::FIXED64:
404 : case proto_field::Type::SFIXED64:
405 1030 : return true;
406 : }
407 :
408 0 : return false;
409 : }
410 :
411 14 : void resolve_types( proto_file & file )
412 : {
413 14 : auto ctx = search_ctx{
414 : .file = file,
415 14 : .message = file.package,
416 : .p_parent = nullptr,
417 14 : };
418 14 : resolve_types( ctx, file.package );
419 14 : }
|