1# Copyright (c) 2024-2025 Huawei Device Co., Ltd. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14require 'set' 15require 'delegate' 16require_relative 'enums.rb' 17 18module Es2pandaLibApi 19 class Arg 20 # Containers 21 @es2panda_arg = nil 22 @lib_args = nil 23 @idl_args = nil 24 @lib_cast = nil 25 @return_args = nil 26 @idl_return_args = nil 27 @is_ast_node = false 28 @is_ast_node_add_children = false 29 @is_ast_type = false 30 @is_ast_type_add_children = false 31 @is_var_type = false 32 @is_enum_type = false 33 @is_scope_type = false 34 @is_code_gen = false 35 @is_decl_type = false 36 @const = '' 37 @base_namespace = '' 38 39 # Flags 40 @need_var_cast = false 41 42 def self.check_ptr_depth(change_type, ptr_depth) 43 !((change_type.es2panda_arg['min_ptr_depth'] && ptr_depth < change_type.es2panda_arg['min_ptr_depth']) || 44 (change_type.es2panda_arg['max_ptr_depth'] && ptr_depth > change_type.es2panda_arg['max_ptr_depth'])) 45 end 46 47 def delete_refs(args) 48 args&.map do |arg| 49 arg['type']['ref_depth'] = 0 if arg['type'].respond_to?('ref_depth') 50 end 51 end 52 53 def delete_ref(type) 54 type['ref_depth'] = 0 if type.respond_to?('ref_depth') 55 end 56 57 def self.const(es2panda_arg) 58 return 'const' if es2panda_arg['type'].respond_to?('prefix') && 59 es2panda_arg['type']['prefix'].include?('const') || 60 es2panda_arg['type'].respond_to?('other_modifiers') && 61 es2panda_arg['type']['other_modifiers'].include?('const') || 62 es2panda_arg['type'].respond_to?('const') && 63 es2panda_arg['type']['const']&.include?('const') 64 end 65 66 def self.set_const_modifier(args, value) 67 args&.map do |arg| 68 unless arg['type']&.respond_to?('const') && arg['type']&.respond_to?('ptr_depth') && 69 arg['type']&.ptr_depth&.positive? 70 arg['type']['const'] = value || '' 71 end 72 end 73 end 74 75 def stop_modify_idl_arg 76 @is_ast_node || @is_ast_node_add_children || @is_ast_type || @is_ast_type_add_children || @is_var_type || 77 @is_scope_type || @is_decl_type 78 end 79 80 def modify_template_nested_arg(arg, base_namespace, idl_mode = false) 81 arg['type'] = ClassData.add_base_namespace(arg['type'], base_namespace) 82 tmp = Arg.new(arg, base_namespace) 83 raise "Unsupported double+ nested complex types: #{arg}\n" if tmp.lib_args.length > 1 84 85 return nil if tmp.lib_args.nil? || tmp.lib_args[0].nil? 86 return arg if idl_mode && tmp.stop_modify_idl_arg 87 88 tmp.lib_args[0]['increase_ptr_depth'] = arg['increase_ptr_depth'] if (arg['increase_ptr_depth'] || 0) != 0 89 tmp.lib_args[0]['type']['const'] = tmp.const 90 tmp.lib_args[0] 91 end 92 93 def self.unsupported_type_msg(es2panda_arg) 94 ptr_depth = es2panda_arg['type'].ptr_depth || 0 95 namespace = es2panda_arg['type'].namespace 96 "'#{namespace ? "#{namespace}::" : ''}"\ 97 "#{es2panda_arg['type'].name}"\ 98 "#{' ' * [1, ptr_depth].min + '*' * ptr_depth}'" 99 end 100 101 def self.is_ast_node(es2panda_arg) 102 Es2pandaLibApi.ast_nodes.include?(es2panda_arg['type'].name) && 103 (!es2panda_arg['type']['namespace'] || es2panda_arg['type']['namespace'] == 'ir') 104 end 105 106 def self.is_ast_node_add_children(es2panda_arg) 107 Es2pandaLibApi.ast_node_additional_children.include?(es2panda_arg['type'].name) && 108 (!es2panda_arg['type']['namespace'] || es2panda_arg['type']['namespace'] == 'ir') 109 end 110 111 def self.is_ast_type(es2panda_arg) 112 Es2pandaLibApi.ast_types.include?(es2panda_arg['type'].name) && 113 (!es2panda_arg['type']['namespace'] || es2panda_arg['type']['namespace'] == 'checker') 114 end 115 116 def self.is_ast_type_add_children(es2panda_arg) 117 Es2pandaLibApi.ast_type_additional_children.include?(es2panda_arg['type'].name) && 118 (!es2panda_arg['type']['namespace'] || 119 es2panda_arg['type']['namespace'] == 'checker') 120 end 121 122 def self.is_var_type(es2panda_arg) 123 Es2pandaLibApi.ast_variables.any? { |variable| variable[1] == es2panda_arg['type'].name } && 124 (!es2panda_arg['type']['namespace'] || es2panda_arg['type']['namespace'] == 'varbinder') 125 end 126 127 def self.is_enum_type(es2panda_arg) 128 Es2pandaLibApi.enums.include?(es2panda_arg['type'].name) 129 end 130 131 def self.is_scope_type(es2panda_arg) 132 Es2pandaLibApi.scopes.include?(es2panda_arg['type'].name) && 133 (!es2panda_arg['type']['namespace'] || es2panda_arg['type']['namespace'] == 'varbinder') 134 end 135 136 def self.is_decl_type(es2panda_arg) 137 Es2pandaLibApi.declarations.include?(es2panda_arg['type'].name) && 138 (!es2panda_arg['type']['namespace'] || es2panda_arg['type']['namespace'] == 'varbinder') 139 end 140 141 def self.is_code_gen(es2panda_arg) 142 Es2pandaLibApi.code_gen_children.include?(es2panda_arg['type'].name) && 143 (!es2panda_arg['type']['namespace'] || es2panda_arg['type']['namespace'] == 'compiler') 144 end 145 146 def self.get_change_type_info(es2panda_arg) 147 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 148 x.es2panda_arg.type.respond_to?('name') && x.es2panda_arg.type.name == es2panda_arg['type']['name'] && 149 (!x.es2panda_arg.type.respond_to?('namespace') || 150 x.es2panda_arg.type.namespace == es2panda_arg['type'].namespace) && 151 (!x.es2panda_arg.type.respond_to?('current_class') || 152 x.es2panda_arg.type['current_class'] == es2panda_arg['type']['current_class']) && 153 Arg.check_ptr_depth(x, es2panda_arg['type']['ptr_depth'] || 0) 154 end 155 156 return found_change_type_link if found_change_type_link 157 158 if Arg.is_ast_type(es2panda_arg) 159 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 160 x.es2panda_arg.type == '|AstType|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 161 end 162 end 163 164 if Arg.is_ast_type_add_children(es2panda_arg) 165 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 166 x.es2panda_arg.type == 167 '|AstTypeAdditionalChildren|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 168 end 169 end 170 171 if Arg.is_ast_node(es2panda_arg) 172 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 173 x.es2panda_arg.type == '|AstNode|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 174 end 175 end 176 177 if Arg.is_ast_node_add_children(es2panda_arg) 178 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 179 x.es2panda_arg.type == 180 '|AstNodeAdditionalChildren|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 181 end 182 end 183 184 if Arg.is_var_type(es2panda_arg) 185 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 186 x.es2panda_arg.type == '|Variable|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 187 end 188 end 189 190 if Arg.is_enum_type(es2panda_arg) 191 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 192 x.es2panda_arg.type == '|Enum|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 193 end 194 end 195 196 if Arg.is_scope_type(es2panda_arg) 197 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 198 x.es2panda_arg.type == '|Scope|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 199 end 200 end 201 202 if Arg.is_decl_type(es2panda_arg) 203 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 204 x.es2panda_arg.type == '|Declaration|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 205 end 206 end 207 208 if Arg.is_code_gen(es2panda_arg) 209 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 210 x.es2panda_arg.type == '|CodeGen|' && Arg.check_ptr_depth(x, es2panda_arg['type'].ptr_depth || 0) 211 end 212 end 213 214 found_change_type_link 215 end 216 217 def self.check_is_type_supported(found_change_type_link, es2panda_arg) 218 unless found_change_type_link || Es2pandaLibApi.primitive_types.include?(es2panda_arg['type'].name) 219 return "Unsupported type: #{Arg.unsupported_type_msg(es2panda_arg)}" 220 end 221 222 if found_change_type_link && !Arg.check_ptr_depth(found_change_type_link, es2panda_arg['type']['ptr_depth'] || 0) 223 return "Invalid ptr depth for type: #{Arg.unsupported_type_msg(es2panda_arg)}" 224 end 225 226 if found_change_type_link && es2panda_arg['name'] == 'returnType' && 227 !found_change_type_link.cast.respond_to?('reverse_cast') 228 return "Unsupported return type: #{Arg.unsupported_type_msg(es2panda_arg)}" 229 end 230 231 if found_change_type_link && es2panda_arg['name'] == 'callType' && 232 !found_change_type_link.cast.respond_to?('call_cast') 233 return "Unsupported call type: #{Arg.unsupported_type_msg(es2panda_arg)}" 234 end 235 236 if found_change_type_link && es2panda_arg['name'] == 'constructorType' && 237 !found_change_type_link.cast.respond_to?('constructor_cast') 238 "Unsupported constructor type: #{Arg.unsupported_type_msg(es2panda_arg)}" 239 end 240 end 241 242 def self.get_change_type_info_with_err_msg(es2panda_arg) 243 found_change_type_link = Arg.get_change_type_info(es2panda_arg) 244 err_msg = Arg.check_is_type_supported(found_change_type_link, es2panda_arg) 245 [found_change_type_link, err_msg] 246 end 247 248 def initialize(arg_info, base_namespace) 249 found_change_type_link, err_msg = Arg.get_change_type_info_with_err_msg(arg_info) 250 raise err_msg unless err_msg.nil? 251 252 @@primitive_types ||= Es2pandaLibApi.primitive_types 253 @es2panda_arg = arg_info 254 @const = Arg.const(arg_info) 255 @es2panda_arg['const'] = @const 256 @base_namespace = base_namespace 257 258 @is_ast_node = Arg.is_ast_node(es2panda_arg) 259 @is_ast_node_add_children = Arg.is_ast_node_add_children(es2panda_arg) 260 @is_ast_type = Arg.is_ast_type(es2panda_arg) 261 @is_ast_type_add_children = Arg.is_ast_type_add_children(es2panda_arg) 262 @is_var_type = Arg.is_var_type(es2panda_arg) 263 @is_enum_type = Arg.is_enum_type(es2panda_arg) 264 @is_scope_type = Arg.is_scope_type(es2panda_arg) 265 @is_decl_type = Arg.is_decl_type(es2panda_arg) 266 @is_code_gen = Arg.is_code_gen(es2panda_arg) 267 268 if found_change_type_link 269 found_change_type = Marshal.load(Marshal.dump(found_change_type_link)) 270 271 placeholders = find_placeholders(found_change_type.es2panda_arg) 272 273 replacements = placeholders.map do |path, placeholder| 274 value = get_value_by_path(@es2panda_arg, path) 275 [placeholder, value] 276 end 277 278 found_change_type.es2panda_arg = Marshal.load(Marshal.dump(@es2panda_arg)) 279 280 if stop_modify_idl_arg 281 found_change_type.idl_args = [Marshal.load(Marshal.dump(@es2panda_arg))] 282 else 283 found_change_type.idl_args = Marshal.load(Marshal.dump(found_change_type.new_args)) 284 found_change_type.idl_return_args = Marshal.load(Marshal.dump(found_change_type.return_args)) 285 end 286 287 found_change_type = deep_replace(found_change_type, replacements) 288 289 clever_replacements = [] 290 template_args = [] 291 292 if found_change_type&.new_args&.length && ((found_change_type.new_args.length > 1 && 293 @es2panda_arg['type'].respond_to?('template_args')) || 294 Es2pandaLibApi.additional_containers.include?(@es2panda_arg['type'].name)) 295 accessor = (@es2panda_arg['type']['ptr_depth'] || 0).zero? ? '.' : '->' 296 clever_replacements += [['|accessor|', accessor]] 297 298 @es2panda_arg['type']['template_args'].each_with_index do |template_arg_raw, i| 299 template_arg = Arg.new({ 'name' => @es2panda_arg['name'] + "Element#{i + 1}", 300 'type' => ClassData.add_base_namespace(template_arg_raw['type'], base_namespace) }, 301 base_namespace) 302 303 raise "Unsupported double+ nested complex types: #{@es2panda_arg}\n" if template_arg.lib_args.length > 1 304 305 unless template_arg_raw['type']['const'].nil? 306 Arg.set_const_modifier(@lib_args, 'const') 307 Arg.set_const_modifier(@idl_args, 'const') 308 end 309 310 template_args += [template_arg] 311 end 312 313 found_change_type&.new_args&.map! do |arg| 314 modify_template_nested_arg(arg, base_namespace) 315 end 316 317 found_change_type&.return_args&.map! do |arg| 318 modify_template_nested_arg(arg, base_namespace) 319 end 320 321 found_change_type&.idl_args&.map! do |arg| 322 modify_template_nested_arg(arg, base_namespace, true) 323 end 324 325 found_change_type&.idl_return_args&.map! do |arg| 326 modify_template_nested_arg(arg, base_namespace, true) 327 end 328 329 correct_depths(found_change_type) 330 331 template_args.each_with_index do |template_arg, i| 332 clever_replacements += [["|template_nested_expression_#{i + 1}|", 333 if template_arg.lib_cast['expression'] 334 template_arg.lib_cast['expression'] + "\n" else '' end]] 335 clever_replacements += [["|reverse_template_nested_expression_#{i + 1}_start|", 336 template_arg.lib_cast['reverse_cast']&.start&.gsub( 337 '?const?', template_arg.const || '') || '']] 338 clever_replacements += [["|reverse_template_nested_expression_#{i + 1}_end|", 339 (template_arg.lib_cast['reverse_cast']&.end || '')]] 340 unless found_change_type.new_args[i].nil? 341 found_change_type.new_args[i]['local_var_name'] = template_arg.lib_cast['var_name'] 342 end 343 end 344 345 @es2panda_arg['type']['ref_depth'] == 2 && 346 found_change_type.cast['var_name'] = 'std::move(' + found_change_type.cast['var_name'] + ')' 347 end 348 349 clever_placeholders = find_placeholders(found_change_type) 350 clever_replacements += clever_placeholders.map do |_path, placeholder| 351 if placeholder.include?('.') 352 value = get_placeholder_value(found_change_type, placeholder) 353 [placeholder, value] 354 else 355 is_known_replacement = clever_replacements.any? do |sub_array| 356 sub_array[0] =~ /_nested_expression_|\|accessor\|/ 357 end 358 raise "Unknown placeholder: #{placeholder}\n" unless is_known_replacement 359 end 360 end 361 362 found_change_type = deep_replace(found_change_type, clever_replacements) 363 364 @lib_args = found_change_type&.new_args 365 @idl_args = found_change_type&.idl_args 366 @lib_cast = found_change_type&.cast 367 @return_args = found_change_type&.return_args 368 @idl_return_args = found_change_type&.idl_return_args 369 nested_arg_transform 370 371 end 372 373 if @lib_args.nil? 374 @lib_args = check_allowed_type(@es2panda_arg) ? [@es2panda_arg] : [] 375 @idl_args = @lib_args 376 end 377 378 delete_refs(@lib_args) 379 delete_refs(@idl_args) 380 381 if @lib_cast.nil? 382 @lib_cast = {} 383 @lib_cast['var_name'] = @es2panda_arg['name'] 384 else 385 @need_var_cast = true 386 end 387 388 return unless @es2panda_arg['name'] == 'returnType' 389 390 Arg.set_const_modifier(@lib_args, @const) 391 Arg.set_const_modifier(@idl_args, @const) 392 end 393 394 def check_allowed_type(arg) 395 @@primitive_types.include?(arg['type'].name) || 396 @is_enum_type || @is_ast_node || @is_ast_node_add_children || @is_ast_type || @is_ast_type_add_children || 397 @is_code_gen || @is_scope_type || @is_var_type 398 end 399 400 def nested_arg_transform 401 return unless @lib_args && @lib_args.size == 1 && !check_allowed_type(@lib_args[0]) 402 403 Arg.set_const_modifier(@lib_args, @const) 404 Arg.set_const_modifier(@idl_args, @const) 405 tmp = Arg.new(@lib_args[0], @base_namespace) 406 @lib_args = tmp.lib_args 407 @idl_args = tmp.idl_args 408 @lib_cast = tmp.lib_cast 409 @return_args = tmp.return_args 410 @idl_return_args = tmp.idl_return_args 411 return if tmp.check_allowed_type(@lib_args[0]) 412 413 nested_arg_transform 414 end 415 416 def find_placeholders(data, path = []) 417 placeholders = [] 418 if data.is_a?(OpenStruct) 419 data.each_pair do |key, value| 420 placeholders += find_placeholders(value, path + [key.to_s]) 421 end 422 elsif data.is_a?(Array) 423 data.each_with_index do |value, index| 424 placeholders += find_placeholders(value, path + [index]) 425 end 426 elsif data.is_a?(String) 427 data.scan(/\|(.+?)\|/) do |match| 428 placeholders << [path.join('.'), '|' + match[0] + '|'] 429 end 430 end 431 placeholders 432 end 433 434 def get_value_by_path(data, path) 435 path.split('.').reduce(data) do |current, key| 436 current.is_a?(Array) ? current[key.to_i] : current[key] 437 end 438 end 439 440 def get_placeholder_value(found_change_type, placeholder) 441 path_to_value = placeholder.gsub('|', '').gsub('_int', '').gsub(' - 1', '') 442 value = get_value_by_path(found_change_type, path_to_value) || 0 443 if value.is_a?(Integer) 444 if placeholder.end_with?('ptr_depth|') 445 value = '*' * value 446 elsif placeholder.end_with?('ref_depth|') 447 value = '&' * value 448 elsif placeholder.end_with?('ptr_depth_int|') 449 value = value 450 elsif placeholder.end_with?('ptr_depth - 1|') 451 value = '*' * (value - 1) 452 elsif placeholder.end_with?('namespace|') 453 value = '' 454 elsif placeholder.end_with?('const|') 455 value = '' 456 else 457 raise "Unknown int found for placeholder '#{placeholder}', value: #{value}\n" 458 end 459 end 460 value 461 end 462 463 # replacements = [[from1, to1], [from2, to2], ...] 464 def deep_replace(data, replacements) 465 if data.is_a?(OpenStruct) 466 data.each_pair do |key, value| 467 data[key] = deep_replace(value, replacements) 468 end 469 elsif data.is_a?(Array) 470 data.map! { |value| deep_replace(value, replacements) } 471 elsif data.is_a?(String) 472 found_replacement = replacements.find { |first, _| first == data } 473 if found_replacement 474 data = if found_replacement[1].is_a?(String) 475 found_replacement[1] 476 else 477 found_replacement[1].dup 478 end 479 else 480 replacements.each { |from, to| data.gsub!(from, to) if to.is_a?(String) } 481 end 482 end 483 data 484 end 485 486 def correct_depths(data) 487 if data.is_a?(OpenStruct) 488 if data.respond_to?(:increase_ptr_depth) 489 ptr_depth = data.type.ptr_depth || 0 490 ptr_depth += data.increase_ptr_depth.to_i 491 data.type['ptr_depth'] = ptr_depth 492 data.delete_field(:increase_ptr_depth) 493 else 494 data.each_pair do |key, value| 495 data[key] = correct_depths(value) 496 end 497 end 498 elsif data.is_a?(Array) 499 data.map! { |value| correct_depths(value) } 500 end 501 data 502 end 503 504 attr_reader :es2panda_arg 505 attr_reader :lib_args 506 attr_reader :idl_args 507 attr_reader :lib_cast 508 attr_reader :return_args 509 attr_reader :idl_return_args 510 attr_reader :const 511 512 def lib_args_to_str 513 @lib_args.map do |lib_arg| 514 Arg.arg_to_str(lib_arg) 515 end&.join(', ') 516 end 517 518 def lib_args_to_idl 519 @idl_args.map do |idl_arg| 520 Arg.arg_to_idl(idl_arg) 521 end.join(', ') 522 end 523 524 def self.arg_value(arg) 525 ptr_depth = arg['type']['ptr_depth'] || 0 526 '*' * ptr_depth + arg['name'] 527 end 528 529 def self.arg_to_str(arg) 530 type_to_str(arg['type']) + arg['name'] 531 end 532 533 def self.type_to_str(type) 534 res = type['const'] ? "#{type['const']} " : '' 535 res += type['name'] 536 ptr_depth = type['ptr_depth'] || 0 537 ref_depth = type['ref_depth'] || 0 538 "#{res} #{'*' * ptr_depth}#{'&' * ref_depth}" 539 end 540 541 def self.arg_to_idl(arg) 542 name = arg['name'] 543 name += '_arg' if %w[optional object readonly sequence].include?(name) 544 "#{type_to_idl(arg['type'])} #{name}" 545 end 546 547 def self.type_to_idl(type) 548 @@replace_to_idl_types ||= { 549 'bool' => 'boolean', 550 'int' => 'i32', 551 'size_t' => 'u32', 552 'char' => 'i8', 553 'int8_t' => 'i8', 554 'uint8_t' => 'u8', 555 'int16_t' => 'i16', 556 'char16_t' => 'i16', 557 'int32_t' => 'i32', 558 'uint32_t' => 'u32', 559 'int64_t' => 'i64', 560 'uint64_t' => 'u64', 561 'float' => 'f32', 562 'double' => 'f64', 563 'sequence<i8>' => 'String' 564 } 565 566 c_type = type['namespace'] && type['namespace'] != 'std' ? "#{type['namespace']}.#{type['name']}" : type['name'] 567 res_sequence_len = @@replace_to_idl_types.key?(c_type) ? (type['ptr_depth'] || 0) : ((type['ptr_depth'] || 1) - 1) 568 res = res_sequence_len.times.reduce(c_type) { |acc, _| "sequence<#{@@replace_to_idl_types[acc] || acc}>" } 569 @@replace_to_idl_types[res] || res 570 end 571 572 attr_reader :is_change_type 573 574 def updater_allowed 575 @is_ast_node 576 end 577 end 578 579 class Type 580 @raw_type = nil 581 @lib_type = nil 582 @return_args = nil 583 @idl_return_args = nil 584 @cast = nil 585 @usage = 'no' 586 @base_namespace = '' 587 588 def initialize(type_info, base_namespace, usage) 589 @raw_type = type_info 590 @base_namespace = base_namespace 591 @usage = usage 592 tmp_arg = Arg.new({ 'name' => "#{usage}Type", 'type' => @raw_type }, base_namespace) 593 594 @return_args = tmp_arg.return_args 595 @idl_return_args = tmp_arg.idl_return_args 596 unless tmp_arg.lib_args.nil? || tmp_arg.lib_args[0].nil? || tmp_arg.lib_args[0]['type'].nil? 597 @lib_type = tmp_arg.lib_args[0]['type'] 598 @idl_type = tmp_arg.idl_args[0]['type'] 599 remove_unused_const_modifier 600 end 601 @cast = tmp_arg.lib_cast 602 end 603 604 attr_reader :usage 605 606 def remove_unused_const_modifier 607 @lib_type['const'] = nil if const && !(@lib_type&.respond_to?('ptr_depth') && @lib_type&.ptr_depth&.positive?) 608 @idl_type['const'] = nil if const && !(@idl_type&.respond_to?('ptr_depth') && @idl_type&.ptr_depth&.positive?) 609 end 610 611 def lib_type_to_str 612 Arg.type_to_str(@lib_type) 613 end 614 615 def idl_type_to_str 616 Arg.type_to_idl(@idl_type) 617 end 618 619 def arena_item_type 620 type = Marshal.load(Marshal.dump(@lib_type)) 621 type['ptr_depth'] -= 1 622 tmp_arg = Arg.new({ 'name' => '', 'type' => type }, @base_namespace) 623 Arg.type_to_str(tmp_arg.lib_args[0]['type']) 624 end 625 626 def call_cast 627 if @cast && usage.include?('call') 628 if @cast['call_cast']['call_var'] 629 @cast['call_cast']['call_var_str'] = Arg.arg_to_str(@cast['call_cast']['call_var']) 630 end 631 632 @cast['call_cast'] 633 else 634 '' 635 end 636 end 637 638 def constructor_cast 639 if @cast && usage.include?('constructor') 640 @cast['constructor_cast'] 641 else 642 '' 643 end 644 end 645 646 def cast 647 if @cast && @cast && usage.include?('return') 648 @cast['reverse_cast'] 649 else 650 '' 651 end 652 end 653 654 def return_args_to_str 655 @return_args ? @return_args.map { |arg| ", #{Arg.arg_to_str(arg)}" }.join('') : '' 656 end 657 658 def return_args_to_idl 659 return '' unless @idl_return_args 660 661 @idl_return_args 662 .reject { |arg| arg['name'] == 'returnTypeLen' } 663 .map { |arg| ", #{Arg.arg_to_idl(arg)}" } 664 .join('') 665 end 666 667 def const 668 @lib_type['const'] 669 end 670 671 attr_reader :raw_type 672 attr_reader :lib_type 673 attr_reader :idl_type 674 end 675 676 class ClassData < SimpleDelegator 677 @class_base_namespace = '' 678 @extends_classname = nil 679 @template_extends = [] 680 @ast_node_type_value = nil 681 682 attr_accessor :class_base_namespace 683 attr_accessor :extends_classname 684 attr_accessor :template_extends 685 attr_accessor :ast_node_type_value 686 687 def class_name 688 Es2pandaLibApi.classes&.each do |_namespace_name, namespace_classes| 689 if namespace_classes&.find { |_name, data| data == self } 690 return namespace_classes&.find { |_name, data| data == self }[0] 691 end 692 end 693 if Es2pandaLibApi.structs.find { |_name, data| data == self }[0] 694 return Es2pandaLibApi.structs.find { |_name, data| data == self }[0] 695 end 696 697 raise 'Unknown class name' 698 end 699 700 def class_c_type 701 change_type_info, err_msg = Arg.get_change_type_info_with_err_msg( 702 { 703 'name' => 'class_c_type', 704 'type' => OpenStruct.new({ 'name' => class_name, 'ptr_depth' => 1, 'namespace' => @class_base_namespace }) 705 } 706 ) 707 708 return '' if err_msg 709 710 c_type = change_type_info.dig('new_args', 0, 'type', 'name') 711 ", c_type=#{c_type}" unless c_type.nil? # In some cases new_args = []. E.g. Checker type. 712 end 713 714 def call_cast 715 name = class_name 716 class_type = Type.new(add_base_namespace(OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 })), 717 class_base_namespace, 'call') 718 class_type.call_cast 719 end 720 721 def constructor_type 722 name = class_name 723 Type.new(add_base_namespace(OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 })), class_base_namespace, 724 'constructor') 725 end 726 727 def constructor_cast 728 name = class_name 729 class_type = Type.new(add_base_namespace(OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 })), 730 class_base_namespace, 'constructor') 731 class_type.constructor_cast 732 end 733 734 def updater_allowed 735 name = class_name 736 class_arg = Arg.new(OpenStruct.new({ 'name' => 'empty', 737 'type' => 738 add_base_namespace(OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 })) }), 739 class_base_namespace) 740 class_arg.updater_allowed 741 end 742 743 def usings_map 744 dig(:usings)&.map { |using| [using['name'], using['type']] }.to_h || {} 745 end 746 747 def check_no_replace_using(_type, new_type) 748 res = false 749 Es2pandaLibApi.no_usings_replace_info['using types']&.each do |using| 750 res ||= Es2pandaLibApi.check_fit(new_type, using) 751 res ||= Es2pandaLibApi.check_fit(_type, using) 752 end 753 !res 754 end 755 756 def replace_with_usings(type, usings) 757 if usings[type&.name] 758 new_type = usings[type.name] 759 return type unless check_no_replace_using(type, new_type) 760 761 new_type['ref_depth'] = type['ref_depth'] || 0 762 new_type['ptr_depth'] = type['ptr_depth'] || 0 763 return new_type 764 end 765 type 766 end 767 768 def add_base_namespace(type) 769 ClassData.add_base_namespace(type, class_base_namespace) 770 end 771 772 def self.add_base_namespace(type, base_namespace) 773 type['namespace'] ||= base_namespace unless Es2pandaLibApi.primitive_types.include?(type['name']) 774 type 775 end 776 777 def error_catch_log(mode, function, err, func_new_name) 778 Es2pandaLibApi.stat_add_unsupported_type(err.message) if err.message.include?('Unsupported type') 779 if mode == 'struct_getter' 780 Es2pandaLibApi.log('warning', "Error: '#{err.message}'") 781 Es2pandaLibApi.log('debug', "Field name: #{function.name}\nField type:\n---\n"\ 782 "#{function.type}\n---\n\n") 783 Es2pandaLibApi.log('backtrace', err.backtrace.join("\n"), "\n\n") 784 Es2pandaLibApi.stat_add_method(0, class_name, class_base_namespace, func_new_name) 785 return 786 end 787 788 Es2pandaLibApi.log('warning', "#{err.message}\n") 789 Es2pandaLibApi.log('debug', "#{mode} name: #{function.name}\n\n"\ 790 "#{mode} raw C++ declaration:\n"\ 791 "```\n#{function.raw_declaration}\n```\n") 792 Es2pandaLibApi.log('backtrace', err.backtrace.join("\n"), "\n\n") 793 794 if mode == 'Constructor' 795 Es2pandaLibApi.stat_add_constructor(0, class_name, class_base_namespace, func_new_name) 796 Es2pandaLibApi.stat_add_class(0, function.name) 797 elsif mode == 'Method' 798 Es2pandaLibApi.stat_add_method(0, class_name, class_base_namespace, func_new_name) 799 else 800 raise 'Unreachable' 801 end 802 end 803 804 def class_constructors 805 res = [] 806 usings = usings_map 807 constructor_overload = {} 808 idl_constructor_overload = {} 809 dig(:constructors)&.each do |constructor| 810 if check_no_gen_constructor(constructor) 811 args = [] 812 begin 813 constructor_cast 814 constructor.args&.each do |arg| 815 arg['type'] = replace_with_usings(arg['type'], usings) 816 arg['type'] = add_base_namespace(arg['type']) 817 arg['type']['current_class'] = constructor.name 818 args << Arg.new(arg, class_base_namespace) 819 end 820 rescue StandardError => e 821 error_catch_log('Constructor', constructor, e, 'Create' + class_name) 822 else 823 Es2pandaLibApi.stat_add_constructor(1, class_name, class_base_namespace, 'Create' + class_name) 824 Es2pandaLibApi.stat_add_class(1, class_name) 825 826 Es2pandaLibApi.log('info', "Supported constructor for class '#{class_name}'\n") 827 828 res << { 'overload' => get_new_method_name(constructor_overload, '', ''), 829 'idl_overload' => get_new_method_name(idl_constructor_overload, '', '', true), 830 'args' => args, 'raw_decl' => constructor.raw_declaration } 831 end 832 else 833 Es2pandaLibApi.log('info', "Banned constructor for class '#{class_name}'\n") 834 end 835 end 836 res 837 end 838 839 def check_no_gen_constructor(constructor) 840 Es2pandaLibApi.ignored_info['postfix_contains']&.each do |postfix| 841 return false if constructor.postfix&.include?(postfix) 842 end 843 constructor.args&.each do |arg| 844 Es2pandaLibApi.ignored_info['args']&.each do |banned_arg_pattern| 845 return false if Es2pandaLibApi.check_fit(arg, banned_arg_pattern) 846 end 847 return false unless Es2pandaLibApi.check_template_type_presents(arg.type) 848 end 849 Es2pandaLibApi.ignored_info['constructors']['call_class']&.each do |call_class| 850 return false if call_class['name'] == class_name 851 end 852 Es2pandaLibApi.ignored_info['template_names']&.each do |template_name| 853 return false if constructor.respond_to?('template') && constructor['template'].include?(template_name['name']) 854 end 855 # NOTE(nikozer) Need to ban ast verifier default condtructor, will be removed later 856 return false if class_name == 'ASTVerifier' && constructor.args&.size == 0 857 858 true 859 end 860 861 def check_no_gen_method(method) 862 return true if Es2pandaLibApi.allowed_info[class_name]&.include?(method.name) 863 return false unless method['template'].nil? 864 865 Es2pandaLibApi.ignored_info['postfix_contains']&.each do |postfix| 866 return false if method.postfix&.include?(postfix) 867 end 868 method.args&.each do |arg| 869 Es2pandaLibApi.ignored_info['args']&.each do |banned_arg_pattern| 870 return false if Es2pandaLibApi.check_fit(arg, banned_arg_pattern) 871 end 872 return false unless Es2pandaLibApi.check_template_type_presents(arg.type) 873 end 874 Es2pandaLibApi.ignored_info['return_type']&.each do |return_type| 875 return false if method.return_type['name'] == return_type['name'] && (!return_type.respond_to?('namespace') || 876 return_type['namespace'] == method.return_type['namespace']) 877 end 878 Es2pandaLibApi.ignored_info['methods']['call_class']&.each do |call_class| 879 return false if call_class['name'] == class_name 880 end 881 return false unless Es2pandaLibApi.check_template_type_presents(method.return_type) 882 883 Es2pandaLibApi.ignored_info['template_names']&.each do |template_name| 884 return false if method.respond_to?('template') && method['template'].include?(template_name['name']) 885 end 886 true 887 end 888 889 def check_no_get_field(field) 890 Es2pandaLibApi.ignored_info['return_type']&.each do |return_type| 891 return false if field.type['name'] == return_type['name'] && (!return_type.respond_to?('namespace') || 892 return_type['namespace'] == field.type['namespace']) 893 end 894 Es2pandaLibApi.ignored_info['methods']['call_class']&.each do |call_class| 895 return false if call_class['name'] == class_name 896 end 897 return false unless Es2pandaLibApi.check_template_type_presents(field.type) 898 899 true 900 end 901 902 def get_return_expr(return_type, call_cast, consts, method, args, function_type) 903 raise 'Unsupported function type' unless %w[method struct_getter].include?(function_type) 904 905 return_expr = '' 906 const, const_return = consts 907 908 if return_type.raw_type['name'] != 'void' && !((return_type.return_args_to_str && 909 !return_type.return_args_to_str.empty?) || 910 Es2pandaLibApi.additional_containers.include?(return_type.raw_type['name'])) 911 return_expr += 'auto apiRes = ' 912 end 913 914 return_expr += return_type.cast['start']&.gsub('?const?', const_return) if return_type.cast 915 916 if function_type == 'method' 917 return_expr += '(' + call_cast['start']&.gsub('?const?', const) + method['name'] + '(' 918 return_expr += args&.map do |arg| 919 arg.lib_cast['var_name'] if arg.lib_cast 920 end&.join(', ') 921 elsif function_type == 'struct_getter' 922 return_expr += '(' + call_cast['start']&.gsub('?const?', const) + method['name'] 923 end 924 925 return_expr += ')' + (call_cast['end']&.gsub('?const?', const) || '') + ')' 926 927 return_expr += return_type.cast['end']&.gsub('?const?', const_return) || '' if return_type.cast 928 929 return_expr += if return_type.raw_type['name'] == 'void' 930 ';' 931 else 932 ";\n\treturn apiRes;" 933 end 934 return_expr 935 end 936 937 def check_for_same_class_name 938 res = false 939 class_name_for_check = class_name 940 Es2pandaLibApi.classes&.each do |_namespace_name, namespace_classes| 941 res |= namespace_classes&.find { |_name, data| data != self && _name == class_name_for_check } 942 end 943 res 944 end 945 946 def get_new_method_name(function_overload, name, const, skip_namespace_overload = false) 947 function_name = if check_for_same_class_name && !skip_namespace_overload then class_base_namespace.capitalize 948 else '' end + name + (const != '' ? 'Const' : '') 949 overload_name = function_name 950 951 if function_overload.include?(function_name) 952 overload_name += function_overload[function_name].to_s 953 function_overload[function_name] += 1 954 else 955 function_overload[function_name] = 1 956 end 957 overload_name 958 end 959 960 def get_const_return_modifier(return_type) 961 if return_type&.const && return_type&.const != '' && return_type&.lib_type&.respond_to?('ptr_depth') && 962 return_type&.lib_type&.ptr_depth&.positive? 963 'const' 964 else 965 '' 966 end 967 end 968 969 def get_const_modifier(method) 970 if method.respond_to?('postfix') && method.postfix.include?('const') 971 'const' 972 else 973 '' 974 end 975 end 976 977 def class_methods 978 res = [] 979 function_overload = {} 980 idl_function_overload = {} 981 usings = usings_map 982 methods = dig(:methods) 983 return res unless methods 984 985 template_extends.each do |template_extend| 986 methods += Es2pandaLibApi.classes['ir'][template_extend]['methods'] 987 end 988 methods.each do |method| 989 if check_no_gen_method(method) 990 begin 991 return_type = Type.new(add_base_namespace(replace_with_usings(method.return_type, usings)), 992 class_base_namespace, 'return') 993 const = get_const_modifier(method) 994 const_return = get_const_return_modifier(return_type) 995 996 args = [] 997 method.args.each do |arg| 998 arg['type'] = add_base_namespace(replace_with_usings(arg['type'], usings)) 999 args << Arg.new(arg, class_base_namespace) 1000 end 1001 1002 return_expr = get_return_expr(return_type, call_cast, [const, const_return], method, args, 'method') 1003 rescue StandardError => e 1004 stat_function_overload = Marshal.load(Marshal.dump(function_overload)) 1005 error_catch_log('Method', method, e, class_name + 1006 get_new_method_name(stat_function_overload, method.name, const)) 1007 else 1008 stat_function_overload = Marshal.load(Marshal.dump(function_overload)) 1009 Es2pandaLibApi.stat_add_method(1, class_name, class_base_namespace, class_name + 1010 get_new_method_name(stat_function_overload, method.name, const)) 1011 Es2pandaLibApi.log('info', 'supported method: ', method.name, ' class: ', class_name, "\n") 1012 1013 res << { 'name' => method.name, 'const' => const, 'return_arg_to_str' => return_type.return_args_to_str, 1014 'overload_name' => get_new_method_name(function_overload, method.name, const), 'args' => args, 1015 'idl_name' => get_new_method_name(idl_function_overload, method.name, const, true), 1016 'return_type' => return_type, 'return_expr' => return_expr, 'raw_decl' => method.raw_declaration, 1017 'const_return' => const_return, 'get_modifier' => method['additional_attributes'] } 1018 end 1019 else 1020 Es2pandaLibApi.log('info', "Banned method\n") 1021 end 1022 end 1023 res 1024 end 1025 1026 def struct_getters 1027 res = [] 1028 usings = usings_map 1029 dig(:members)&.each do |field| 1030 if check_no_get_field(field) 1031 begin 1032 return_type = Type.new(add_base_namespace(replace_with_usings(field.type, usings)), 1033 class_base_namespace, 'return') 1034 const = '' 1035 const_return = get_const_return_modifier(return_type) 1036 1037 return_expr = get_return_expr(return_type, call_cast, [const, const_return], field, nil, 'struct_getter') 1038 rescue StandardError => e 1039 error_catch_log('struct_getter', field, e, class_name + field.name) 1040 else 1041 Es2pandaLibApi.stat_add_method(1, class_name, class_base_namespace, class_name + field.name) 1042 Es2pandaLibApi.log('info', 'supported struct getter: ', field.name, ' struct: ', class_name, "\n") 1043 1044 res << { 'name' => field.name, 'return_arg_to_str' => return_type.return_args_to_str, 1045 'return_type' => return_type, 'return_expr' => return_expr, 'const_return' => const_return } 1046 end 1047 else 1048 Es2pandaLibApi.log('info', "Banned method\n") 1049 end 1050 end 1051 res 1052 end 1053 end 1054 1055 @ast_nodes = Set.new(%w[AstNode Expression Statement TypeNode]) 1056 @ast_node_mapping = [] 1057 @ast_types = Set.new(['Type']) 1058 @scopes = Set.new(%w[Scope VariableScope]) 1059 @declarations = Set.new(['Decl']) 1060 @classes = {} 1061 @structs = {} 1062 @includes = Set.new 1063 @change_types = [] 1064 @ignored_info = {} 1065 @allowed_info = {} 1066 @enums = Set.new(%w[AstNodeType ScopeType DeclType]) 1067 1068 @all_methods = 0.0 1069 @all_constructors = 0.0 1070 @all_classes = Set.new 1071 @classes_with_supported_constructor = Set.new 1072 @unsupported_types = {} 1073 1074 @supported_methods = 0.0 1075 @supported_method_type = {} 1076 @supported_constructors = 0.0 1077 @supported_constructor_type = {} 1078 1079 @info_log = false 1080 @debug_log = false 1081 @warning_log = true 1082 @backtrace_log = false 1083 @stat_log = false 1084 @type_stat_log = false 1085 1086 def log(debug_level, *args) 1087 if debug_level == 'info' && @info_log 1088 print args.join('').to_s 1089 elsif debug_level == 'debug' && @debug_log 1090 print args.join('').to_s 1091 elsif debug_level == 'warning' && @warning_log 1092 print "WARNING:[e2p_api_generator]: #{args.join('')}" 1093 elsif debug_level == 'backtrace' && @backtrace_log 1094 print args.join('').to_s 1095 elsif debug_level == 'stat' && @stat_log 1096 print args.join('').to_s 1097 elsif debug_level == 'type_stat' && @type_stat_log 1098 print args.join('').to_s 1099 end 1100 end 1101 1102 def no_usings_replace_info 1103 { 'using types' => [ 1104 { 'name' => 'function', 'namespace' => 'std' }, 1105 { 'name' => 'ExternalSource', 'namespace' => 'parser' }, 1106 { 'name' => 'DirectExternalSource', 'namespace' => 'parser' }, 1107 { 'name' => 'ETSNolintsCollectionMap', 'namespace' => 'parser' } 1108 ] } 1109 end 1110 1111 def check_fit(data, pattern) 1112 return true if data.nil? || pattern.nil? 1113 1114 if pattern.is_a?(OpenStruct) || pattern.is_a?(Hash) 1115 pattern.each_pair do |key, value| 1116 return false unless check_fit(data[key], value) 1117 end 1118 elsif pattern.is_a?(Array) 1119 pattern.each_with_index do |value, i| 1120 return false unless check_fit(data[i], value) 1121 end 1122 elsif pattern.is_a?(String) || pattern.is_a?(Integer) 1123 return false if data != pattern 1124 end 1125 1126 true 1127 end 1128 1129 def check_template_type_presents(type) 1130 type['template_args']&.each do |template_arg| 1131 return false unless Es2pandaLibApi.allowed_info['template_types'].include?(template_arg.type.name) 1132 1133 if template_arg.type.respond_to?('template_args') 1134 Es2pandaLibApi.log('warning', 'Unexpected nested template args in type!') 1135 return false 1136 end 1137 end 1138 true 1139 end 1140 1141 def check_class_type(class_name, class_base_namespace) 1142 type = '' 1143 if ast_nodes.include?(class_name) && class_base_namespace == 'ir' 1144 type = 'AST manipulation' 1145 elsif ast_node_additional_children.include?(class_name) && class_base_namespace == 'ir' 1146 type = 'AST manipulation' 1147 elsif class_base_namespace == 'ir' 1148 type = 'AST manipulation' 1149 elsif ast_types.include?(class_name) && class_base_namespace == 'checker' 1150 type = 'Type manipulation' 1151 elsif ast_type_additional_children.include?(class_name) && class_base_namespace == 'checker' 1152 type = 'Type manipulation' 1153 elsif class_base_namespace == 'checker' 1154 type = 'Type check' 1155 elsif ast_variables.find { |x| x[1] == class_name } && class_base_namespace == 'varbinder' 1156 type = 'Variable manipulation' 1157 elsif scopes.include?(class_name) && class_base_namespace == 'varbinder' 1158 type = 'Scope manipulation' 1159 elsif declarations.include?(class_name) && class_base_namespace == 'varbinder' 1160 type = 'Declaration' 1161 elsif class_base_namespace == 'varbinder' 1162 type = 'Varbinder manipulation' 1163 elsif class_base_namespace == 'parser' 1164 type = 'Parser manipulation' 1165 elsif class_base_namespace == 'compiler::ast_verifier' 1166 type = 'AST Verifier functions' 1167 elsif class_base_namespace == 'util' 1168 type = 'Import path manager' 1169 elsif class_base_namespace == 'gen' 1170 type = 'Compiler options' 1171 elsif class_base_namespace == 'es2panda' 1172 type = 'Arkts config' 1173 else 1174 raise 'Unsupported class type for stats class name: "' + 1175 class_name + '" class namespace: "' + class_base_namespace + '"' 1176 end 1177 type 1178 end 1179 1180 def stat_add_method_type(class_name, class_base_namespace, func_new_name) 1181 type = check_class_type(class_name, class_base_namespace) 1182 if @supported_method_type[type] 1183 @supported_method_type[type][0] += 1 1184 @supported_method_type[type][1] << func_new_name 1185 else 1186 @supported_method_type[type] = [1, [func_new_name]] 1187 end 1188 end 1189 1190 def stat_add_constructor_type(class_name, class_base_namespace, constructor_new_name) 1191 type = check_class_type(class_name, class_base_namespace) 1192 if @supported_constructor_type[type] 1193 @supported_constructor_type[type][0] += 1 1194 @supported_constructor_type[type][1] << constructor_new_name 1195 else 1196 @supported_constructor_type[type] = [1, [constructor_new_name]] 1197 end 1198 end 1199 1200 def stat_add_method(support, class_name, class_base_namespace, func_new_name) 1201 @all_methods += 1 1202 @supported_methods += support 1203 stat_add_method_type(class_name, class_base_namespace, func_new_name) if support == 1 1204 end 1205 1206 def stat_add_constructor(support, class_name, class_base_namespace, constructor_new_name) 1207 @all_constructors += 1 1208 @supported_constructors += support 1209 stat_add_constructor_type(class_name, class_base_namespace, constructor_new_name) if support == 1 1210 end 1211 1212 def stat_add_class(support, class_name) 1213 @all_classes << class_name 1214 @classes_with_supported_constructor << class_name if support != 0 1215 end 1216 1217 def stat_add_unsupported_type(err_msg) 1218 @unsupported_types[err_msg] ||= 0 1219 @unsupported_types[err_msg] += 1 1220 end 1221 1222 def print_stats 1223 Es2pandaLibApi.log('stat', 1224 "--------------\n"\ 1225 "Es2panda API generated:\n"\ 1226 " - Methods: #{@supported_methods} / #{@all_methods} ( #{(@supported_methods / @all_methods * 100).round(2)} % )\n"\ 1227 " - Constructors: #{@supported_constructors} / #{@all_constructors} ( "\ 1228 "#{(@supported_constructors / @all_constructors * 100).round(2)} % )\n"\ 1229 " - Classes: #{@classes_with_supported_constructor.size} / #{@all_classes.size} ( "\ 1230 "#{(@classes_with_supported_constructor.size.to_f / @all_classes.size * 100).round(2)} % )\n"\ 1231 "--------------\n") 1232 end 1233 1234 def ast_variables 1235 [%w[NO Variable], 1236 %w[LOCAL LocalVariable], 1237 %w[GLOBAL GlobalVariable], 1238 %w[MODULE ModuleVariable], 1239 %w[ENUM EnumVariable]] 1240 end 1241 1242 def ast_type_additional_children 1243 %w[ 1244 ETSStringType 1245 ETSDynamicType 1246 ETSAsyncFuncReturnType 1247 ETSDynamicFunctionType 1248 ETSEnumType 1249 ETSBigIntType 1250 ] 1251 end 1252 1253 def additional_containers 1254 %w[ 1255 shared_ptr 1256 ] 1257 end 1258 1259 def additional_classes_to_generate 1260 %w[ 1261 TypeRelation 1262 GlobalTypesHolder 1263 Checker 1264 ETSChecker 1265 CheckerContext 1266 ResolveResult 1267 SignatureInfo 1268 Signature 1269 VarBinder 1270 ETSBinder 1271 RecordTable 1272 BoundContext 1273 ETSParser 1274 ASTVerifier 1275 CheckMessage 1276 Program 1277 ImportPathManager 1278 Options 1279 ArkTsConfig 1280 Path 1281 ] 1282 end 1283 1284 def ast_node_additional_children 1285 %w[ 1286 TypedStatement 1287 ClassElement 1288 AnnotatedExpression 1289 Literal 1290 LoopStatement 1291 MaybeOptionalExpression 1292 Property 1293 ] 1294 end 1295 1296 def code_gen_children 1297 %w[ 1298 CodeGen 1299 PandaGen 1300 ETSGen 1301 ] 1302 end 1303 1304 def template_extends_classes 1305 %w[Annotated Typed AnnotationAllowed JsDocAllowed] 1306 end 1307 1308 def primitive_types 1309 %w[ 1310 char 1311 char16_t 1312 short 1313 int 1314 long 1315 long long 1316 float 1317 double 1318 long double 1319 bool 1320 void 1321 size_t 1322 uint8_t 1323 uint32_t 1324 uint64_t 1325 int8_t 1326 int16_t 1327 int32_t 1328 int64_t 1329 es2panda_Program 1330 es2panda_ExternalSource 1331 es2panda_AstNode 1332 es2panda_FunctionSignature 1333 es2panda_SourcePosition 1334 es2panda_SourceRange 1335 es2panda_SrcDumper 1336 es2panda_AstDumper 1337 es2panda_LabelPair 1338 es2panda_ScriptFunctionData 1339 es2panda_ImportSource 1340 es2panda_Signature 1341 es2panda_SignatureInfo 1342 es2panda_CheckerContext 1343 es2panda_ResolveResult 1344 es2panda_ValidationInfo 1345 es2panda_Type 1346 es2panda_TypeRelation 1347 es2panda_IndexInfo 1348 es2panda_GlobalTypesHolder 1349 es2panda_ObjectDescriptor 1350 es2panda_Variable 1351 es2panda_Scope 1352 es2panda_Declaration 1353 es2panda_RecordTable 1354 es2panda_AstVisitor 1355 es2panda_CodeGen 1356 es2panda_VReg 1357 NodeTraverser 1358 NodeTransformer 1359 NodePredicate 1360 es2panda_Config 1361 es2panda_Context 1362 PropertyProcessor 1363 PropertyTraverser 1364 ClassBuilder 1365 MethodBuilder 1366 ClassInitializerBuilder 1367 es2panda_variantDoubleCharArrayBool 1368 es2panda_variantIndex 1369 es2panda_DynamicImportData 1370 es2panda_IRNode 1371 es2panda_ScopeFindResult 1372 es2panda_BindingProps 1373 es2panda_BoundContext 1374 es2panda_ErrorLogger 1375 es2panda_ArkTsConfig 1376 es2panda_VerificationContext 1377 es2panda_AstVerifier 1378 es2panda_VerifierMessage 1379 es2panda_ImportPathManager 1380 es2panda_Options 1381 es2panda_Path 1382 es2panda_OverloadInfo 1383 es2panda_JsDocRecord 1384 es2panda_JsDocInfo 1385 ] 1386 end 1387 1388 attr_reader :ast_nodes 1389 attr_reader :ast_types 1390 attr_reader :scopes 1391 attr_reader :declarations 1392 attr_reader :classes 1393 attr_reader :structs 1394 attr_reader :includes 1395 attr_reader :enums 1396 attr_reader :change_types 1397 attr_reader :ignored_info 1398 attr_reader :allowed_info 1399 1400 def deep_to_h(object) 1401 case object 1402 when OpenStruct 1403 object.to_h.transform_keys(&:to_s).transform_values { |value| deep_to_h(value) } 1404 when Array 1405 object.map { |element| deep_to_h(element) } 1406 else 1407 object 1408 end 1409 end 1410 1411 def extends_to_idl(extends) 1412 return nil unless extends && !extends.include?(',') 1413 1414 if extends.include?('std::') 1415 Es2pandaLibApi.log('warning', "Unsupported inheritance from '#{extends}'\n") 1416 return nil 1417 end 1418 1419 extends.gsub(/[<>]/, ' ').split.last.split('::').last 1420 end 1421 1422 def template_extends(extends) 1423 res = [] 1424 return res unless extends 1425 1426 template_extends_classes.each do |extend| 1427 res << extend if extends.include?(extend) 1428 end 1429 res 1430 end 1431 1432 def extract_classes_in_namespace(data, namespace_name, &is_class_needed) 1433 @classes[namespace_name] = {} unless @classes[namespace_name] 1434 data[namespace_name]&.class_definitions&.each do |class_definition| 1435 if is_class_needed.call(class_definition.name) 1436 class_data = ClassData.new(class_definition&.public) 1437 class_data.class_base_namespace = namespace_name 1438 class_data.extends_classname = extends_to_idl(class_definition.extends) 1439 class_data.template_extends = [] 1440 @classes[namespace_name][class_definition.name] = class_data 1441 end 1442 end 1443 end 1444 1445 def wrap_data(data) 1446 return unless data 1447 1448 data.macros&.each do |macros| 1449 case macros.name 1450 when 'AST_NODE_MAPPING' 1451 @ast_nodes.merge(Set.new(macros.values.map { |x| x[1] })) 1452 @ast_node_mapping.concat(macros.values) 1453 when 'AST_NODE_REINTERPRET_MAPPING' 1454 @ast_nodes.merge(Set.new(macros.values.flat_map { |x| x[2..3] })) 1455 @ast_node_mapping.concat(macros.values.flat_map { |x| [[x[0], x[2]], [x[1], x[3]]] }) 1456 when 'TYPE_MAPPING' 1457 @ast_types.merge(Set.new(macros.values.flat_map { |x| x[1] })) 1458 end 1459 end 1460 1461 data.varbinder&.macros&.each do |macros| 1462 case macros.name 1463 when 'SCOPE_TYPES' 1464 @scopes.merge(Set.new(macros.values.map { |x| x[1] })) 1465 when 'DECLARATION_KINDS' 1466 @declarations.merge(Set.new(macros.values.map { |x| x[1] })) 1467 end 1468 end 1469 1470 data.ast_node_reinterpret_mapping&.each do |mapping| 1471 @ast_nodes << mapping[2] 1472 @ast_nodes << mapping[3] 1473 end 1474 1475 data.paths&.each do |include| 1476 @includes << include 1477 end 1478 1479 data.change_types&.each do |change_type| 1480 @change_types << change_type 1481 end 1482 1483 @ignored_info = deep_to_h(data.ignored_list) if data.ignored_list 1484 @allowed_info = deep_to_h(data.allowed_list) if data.allowed_list 1485 1486 data.enums&.each do |enum| 1487 @enums << enum.name 1488 end 1489 Enums.wrap_data(data) 1490 1491 @classes['ir'] = {} unless @classes['ir'] 1492 data['ir']&.class_definitions&.each do |class_definition| 1493 class_data = ClassData.new(class_definition&.public) 1494 class_data.class_base_namespace = 'ir' 1495 class_data.extends_classname = extends_to_idl(class_definition.extends) 1496 class_data.template_extends = template_extends(class_definition.extends) 1497 flag_name = @ast_node_mapping.find { |elem| elem[1] == class_definition.name }&.first 1498 class_data.ast_node_type_value = Enums.get_astnodetype_value(flag_name) 1499 next if class_definition.name == 'NumberLiteral' # it is defined manually 1500 1501 @classes['ir'][class_definition.name] = class_data 1502 end 1503 1504 extract_classes_in_namespace(data, 'checker') { |class_name| 1505 @ast_types.include?(class_name) || ast_type_additional_children.include?(class_name) || 1506 additional_classes_to_generate.include?(class_name) 1507 } 1508 extract_classes_in_namespace(data, 'varbinder') { |class_name| 1509 scopes.include?(class_name) || declarations.include?(class_name) || 1510 ast_variables.find { |x| x[1] == class_name } || additional_classes_to_generate.include?(class_name) 1511 } 1512 extract_classes_in_namespace(data, 'parser') { |class_name| additional_classes_to_generate.include?(class_name) } 1513 extract_classes_in_namespace(data, 'util') { |class_name| additional_classes_to_generate.include?(class_name) } 1514 extract_classes_in_namespace(data, 'gen') { |class_name| additional_classes_to_generate.include?(class_name) } 1515 extract_classes_in_namespace(data, 'es2panda') { |class_name| additional_classes_to_generate.include?(class_name) } 1516 1517 @classes['ast_verifier'] = {} unless @classes['ast_verifier'] 1518 data['ast_verifier']&.class_definitions&.each do |class_definition| 1519 if additional_classes_to_generate.include?(class_definition.name) 1520 class_data = ClassData.new(class_definition&.public) 1521 class_data.class_base_namespace = 'compiler::ast_verifier' 1522 class_data.template_extends = [] 1523 @classes['ast_verifier'][class_definition.name] = class_data 1524 end 1525 end 1526 end 1527 1528 module_function :wrap_data, :classes, :ast_nodes, :includes, :change_types, :enums, :ast_types, :check_fit, :log, 1529 :stat_add_constructor, :stat_add_method, :print_stats, :ignored_info, :allowed_info, :primitive_types, 1530 :stat_add_class, :stat_add_unsupported_type, :ast_node_additional_children, :code_gen_children, 1531 :additional_classes_to_generate, :ast_type_additional_children, :scopes, :ast_variables, :deep_to_h, 1532 :no_usings_replace_info, :declarations, :check_template_type_presents, :structs, 1533 :additional_containers, :stat_add_constructor_type, :stat_add_method_type, :check_class_type, 1534 :extends_to_idl, :template_extends, :template_extends_classes, :extract_classes_in_namespace 1535end 1536 1537def Gen.on_require(data) 1538 Es2pandaLibApi.wrap_data(data) 1539end 1540