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