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_var_type = false 31 @is_enum_type = false 32 @is_scope_type = false 33 @is_code_gen = false 34 35 # Flags 36 @need_var_cast = false 37 38 def check_ptr_depth(change_type, ptr_depth) 39 !((change_type.es2panda_arg['min_ptr_depth'] && ptr_depth < change_type.es2panda_arg['min_ptr_depth']) || 40 (change_type.es2panda_arg['max_ptr_depth'] && ptr_depth > change_type.es2panda_arg['max_ptr_depth'])) 41 end 42 43 def delete_ref(type) 44 type['ref_depth'] = 0 if type.respond_to?('ref_depth') 45 end 46 47 def initialize(arg_info) 48 @primitive_types = [ 49 'char', 50 'char16_t', 51 'short', 52 'int', 53 'long', 54 'long long', 55 'float', 56 'double', 57 'long double', 58 'bool', 59 'void', 60 'size_t', 61 'uint32_t', 62 'int32_t', 63 'int64_t', 64 'es2panda_AstNode', 65 'es2panda_Type', 66 'es2panda_Scope' 67 ] 68 @es2panda_arg = arg_info 69 70 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 71 x.es2panda_arg.type.respond_to?('name') && x.es2panda_arg.type.name == @es2panda_arg['type']['name'] && 72 (!x.es2panda_arg.type.respond_to?('namespace') || 73 x.es2panda_arg.type.namespace == @es2panda_arg['type'].namespace) && 74 (!x.es2panda_arg.type.respond_to?('current_class') || 75 x.es2panda_arg.type['current_class'] == @es2panda_arg['type']['current_class']) && 76 check_ptr_depth(x, @es2panda_arg['type']['ptr_depth'] || 0) 77 end 78 79 unless found_change_type_link 80 @is_ast_node = Es2pandaLibApi.ast_nodes.include?(@es2panda_arg['type'].name) && 81 (!@es2panda_arg['type']['namespace'] || @es2panda_arg['type']['namespace'] == 'ir') 82 @is_ast_node_add_children = Es2pandaLibApi.ast_node_additional_children.include?(@es2panda_arg['type'].name) && 83 (!@es2panda_arg['type']['namespace'] || @es2panda_arg['type']['namespace'] == 'ir') 84 @is_ast_type = Es2pandaLibApi.ast_types.include?(@es2panda_arg['type'].name) && 85 (!@es2panda_arg['type']['namespace'] || @es2panda_arg['type']['namespace'] == 'checker') 86 @is_var_type = Es2pandaLibApi.ast_variables.any? { |variable| variable[1] == @es2panda_arg['type'].name } && 87 (!@es2panda_arg['type']['namespace'] || @es2panda_arg['type']['namespace'] == 'varbinder') 88 @is_enum_type = Es2pandaLibApi.enums.include?(@es2panda_arg['type'].name) 89 @is_scope_type = Es2pandaLibApi.scopes.include?(@es2panda_arg['type'].name) && 90 (!@es2panda_arg['type']['namespace'] || @es2panda_arg['type']['namespace'] == 'varbinder') 91 @is_code_gen = Es2pandaLibApi.code_gen_children.include?(@es2panda_arg['type'].name) && 92 (!@es2panda_arg['type']['namespace'] || @es2panda_arg['type']['namespace'] == 'compiler') 93 94 if @is_ast_type 95 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 96 x.es2panda_arg.type == '|AstType|' && check_ptr_depth(x, @es2panda_arg['type'].ptr_depth || 0) 97 end 98 end 99 100 if @is_ast_node 101 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 102 x.es2panda_arg.type == '|AstNode|' && check_ptr_depth(x, @es2panda_arg['type'].ptr_depth || 0) 103 end 104 end 105 106 if @is_ast_node_add_children 107 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 108 x.es2panda_arg.type == 109 '|AstNodeAdditionalChildren|' && check_ptr_depth(x, @es2panda_arg['type'].ptr_depth || 0) 110 end 111 end 112 113 if @is_var_type 114 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 115 x.es2panda_arg.type == '|Variable|' && check_ptr_depth(x, @es2panda_arg['type'].ptr_depth || 0) 116 end 117 end 118 119 if @is_enum_type 120 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 121 x.es2panda_arg.type == '|Enum|' && check_ptr_depth(x, @es2panda_arg['type'].ptr_depth || 0) 122 end 123 end 124 125 if @is_scope_type 126 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 127 x.es2panda_arg.type == '|Scope|' && check_ptr_depth(x, @es2panda_arg['type'].ptr_depth || 0) 128 end 129 end 130 131 if @is_code_gen 132 found_change_type_link = Es2pandaLibApi.change_types.find do |x| 133 x.es2panda_arg.type == '|CodeGen|' && check_ptr_depth(x, @es2panda_arg['type'].ptr_depth || 0) 134 end 135 end 136 end 137 138 unless found_change_type_link || @primitive_types.include?(@es2panda_arg['type'].name) 139 raise "Unsupported type: '" + @es2panda_arg['type'].name + "' ptr depth: " + 140 (@es2panda_arg['type'].ptr_depth || 0).to_s 141 end 142 143 ptr_depth = @es2panda_arg['type']['ptr_depth'] || 0 144 145 if found_change_type_link && !check_ptr_depth(found_change_type_link, ptr_depth) 146 raise 'invalid ptr_depth: ' + ptr_depth.to_s + ', type: ' + @es2panda_arg['type'].name 147 end 148 149 if found_change_type_link && @es2panda_arg['name'] == 'returnType' && 150 !found_change_type_link.cast.respond_to?('reverse_cast') 151 raise 'Unsupported return type: ' + @es2panda_arg['type'].name + ' ptr depth: ' + 152 (@es2panda_arg['type'].ptr_depth || 0).to_s 153 end 154 155 if found_change_type_link 156 found_change_type = Marshal.load(Marshal.dump(found_change_type_link)) 157 158 placeholders = find_placeholders(found_change_type.es2panda_arg) 159 160 replacements = placeholders.map do |path, placeholder| 161 value = get_value_by_path(@es2panda_arg, path) 162 [placeholder, value] 163 end 164 165 found_change_type.es2panda_arg = Marshal.load(Marshal.dump(@es2panda_arg)) 166 found_change_type = deep_replace(found_change_type, replacements) 167 168 clever_replacements = [] 169 template_args = [] 170 171 if found_change_type&.new_args&.length && found_change_type.new_args.length > 1 && 172 @es2panda_arg['type'].respond_to?('template_args') 173 accessor = (@es2panda_arg['type']['ptr_depth'] || 0).zero? ? '.' : '->' 174 clever_replacements += [['|accessor|', accessor]] 175 176 @es2panda_arg['type']['template_args'].each_with_index do |template_arg_raw, i| 177 template_arg = Arg.new({ 'name' => @es2panda_arg['name'] + "Element#{i + 1}", 178 'type' => template_arg_raw['type'] }) 179 raise 'Unsupported double+ nested complex types: ' + @es2panda_arg.to_s if template_arg.lib_args.length > 1 180 181 template_args += [template_arg] 182 end 183 184 found_change_type&.new_args&.map! do |arg| 185 tmp = Arg.new(arg) 186 raise 'Unsupported double+ nested complex types: ' + arg_info.to_s if tmp.lib_args.length > 1 187 188 tmp.lib_args[0]['increase_ptr_depth'] = arg['increase_ptr_depth'] if (arg['increase_ptr_depth'] || 0) != 0 189 tmp.lib_args[0] 190 end 191 192 found_change_type&.return_args&.map! do |arg| 193 tmp = Arg.new(arg) 194 raise 'Unsupported double+ nested complex types: ' + arg_info if tmp.lib_args.length > 1 195 196 tmp.lib_args[0]['increase_ptr_depth'] = arg['increase_ptr_depth'] if (arg['increase_ptr_depth'] || 0) != 0 197 tmp.lib_args[0] 198 end 199 correct_depths(found_change_type) 200 201 template_args.each_with_index do |template_arg, i| 202 clever_replacements += [["|template_nested_expression_#{i + 1}|", 203 template_arg.lib_cast['expression'] + "\n"]] 204 clever_replacements += [["|reverse_template_nested_expression_#{i + 1}_start|", 205 template_arg.lib_cast['reverse_cast']&.start&.gsub('?const?', '') || '']] 206 clever_replacements += [["|reverse_template_nested_expression_#{i + 1}_end|", 207 (template_arg.lib_cast['reverse_cast']&.end || '')]] 208 found_change_type.new_args[i]['local_var_name'] = template_arg.lib_cast['var_name'] 209 end 210 211 @es2panda_arg['type']['ref_depth'] == 2 && 212 found_change_type.cast['var_name'] = 'std::move(' + found_change_type.cast['var_name'] + ')' 213 end 214 215 clever_placeholders = find_placeholders(found_change_type) 216 clever_replacements += clever_placeholders.map do |_path, placeholder| 217 if placeholder.include?('.') 218 value = get_placeholder_value(found_change_type, placeholder) 219 [placeholder, value] 220 else 221 is_known_replacement = clever_replacements.any? do |sub_array| 222 sub_array[0] =~ /_nested_expression_|\|accessor\|/ 223 end 224 raise 'Unknown placeholder: ' + placeholder + "\n" unless is_known_replacement 225 end 226 end 227 228 found_change_type = deep_replace(found_change_type, clever_replacements) 229 230 @lib_args = found_change_type&.new_args 231 @lib_cast = found_change_type&.cast 232 @return_args = found_change_type&.return_args || nil 233 234 end 235 236 if @lib_args.nil? 237 @lib_args = [] 238 @lib_args << @es2panda_arg 239 end 240 241 @lib_args.each do |arg| 242 delete_ref(arg['type']) 243 end 244 245 if @lib_cast.nil? 246 @lib_cast = {} 247 @lib_cast['var_name'] = @es2panda_arg['name'] 248 else 249 @need_var_cast = true 250 end 251 end 252 253 def find_placeholders(data, path = []) 254 placeholders = [] 255 if data.is_a?(OpenStruct) 256 data.each_pair do |key, value| 257 placeholders += find_placeholders(value, path + [key.to_s]) 258 end 259 elsif data.is_a?(Array) 260 data.each_with_index do |value, index| 261 placeholders += find_placeholders(value, path + [index]) 262 end 263 elsif data.is_a?(String) 264 data.scan(/\|(.+?)\|/) do |match| 265 placeholders << [path.join('.'), '|' + match[0] + '|'] 266 end 267 end 268 placeholders 269 end 270 271 def get_value_by_path(data, path) 272 path.split('.').reduce(data) do |current, key| 273 current.is_a?(Array) ? current[key.to_i] : current[key] 274 end 275 end 276 277 def get_placeholder_value(found_change_type, placeholder) 278 path_to_value = placeholder.gsub('|', '').gsub('_int', '').gsub(' - 1', '') 279 value = get_value_by_path(found_change_type, path_to_value) || 0 280 if value.is_a?(Integer) 281 if placeholder.end_with?('ptr_depth|') 282 value = '*' * value 283 elsif placeholder.end_with?('ref_depth|') 284 value = '&' * value 285 elsif placeholder.end_with?('ptr_depth_int|') 286 value = value 287 elsif placeholder.end_with?('ptr_depth - 1|') 288 value = '*' * (value - 1) 289 elsif placeholder.end_with?('namespace|') 290 value = '' 291 else 292 raise 'Unknown integer found for placeholer: ' + placeholder + ', res: ' + value.to_s + "\n" 293 end 294 end 295 value 296 end 297 298 # replacements = [[from1, to1], [from2, to2], ...] 299 def deep_replace(data, replacements) 300 if data.is_a?(OpenStruct) 301 data.each_pair do |key, value| 302 data[key] = deep_replace(value, replacements) 303 end 304 elsif data.is_a?(Array) 305 data.map! { |value| deep_replace(value, replacements) } 306 elsif data.is_a?(String) 307 found_replacement = replacements.find { |first, _| first == data } 308 if found_replacement 309 data = if found_replacement[1].is_a?(String) 310 found_replacement[1] 311 else 312 found_replacement[1].dup 313 end 314 else 315 replacements.each { |from, to| data.gsub!(from, to) if to.is_a?(String) } 316 end 317 end 318 data 319 end 320 321 def correct_depths(data) 322 if data.is_a?(OpenStruct) 323 if data.respond_to?(:increase_ptr_depth) 324 ptr_depth = data.type.ptr_depth || 0 325 ptr_depth += data.increase_ptr_depth.to_i 326 data.type['ptr_depth'] = ptr_depth 327 data.delete_field(:increase_ptr_depth) 328 else 329 data.each_pair do |key, value| 330 data[key] = correct_depths(value) 331 end 332 end 333 elsif data.is_a?(Array) 334 data.map! { |value| correct_depths(value) } 335 end 336 data 337 end 338 339 attr_reader :es2panda_arg 340 341 attr_reader :lib_args 342 343 attr_reader :lib_cast 344 345 attr_reader :return_args 346 347 def lib_args_to_str 348 @lib_args.map do |lib_arg| 349 Arg.arg_to_str(lib_arg) 350 end&.join(', ') 351 end 352 353 def self.arg_value(arg) 354 ptr_depth = arg['type']['ptr_depth'] || 0 355 '*' * ptr_depth + arg['name'] 356 end 357 358 def self.arg_to_str(arg) 359 type_to_str(arg['type']) + arg['name'] 360 end 361 362 def self.type_to_str(type) 363 res = type['name'] 364 ptr_depth = type['ptr_depth'] || 0 365 ref_depth = type['ref_depth'] || 0 366 res + ' ' + '*' * ptr_depth + '&' * ref_depth 367 end 368 369 attr_reader :is_change_type 370 371 def updater_allowed 372 @is_ast_type || @is_ast_node || @is_ast_node_add_children || @is_var_type || @is_enum_type || 373 @is_scope_type || @is_code_gen 374 end 375 end 376 377 class Type 378 @raw_type = nil 379 @lib_type = nil 380 @return_args = nil 381 @cast = nil 382 383 def initialize(type_info) 384 @raw_type = type_info 385 tmp_arg = Arg.new({ 'name' => 'returnType', 'type' => @raw_type }) 386 387 @return_args = tmp_arg.return_args if tmp_arg.lib_args.length != 1 388 @lib_type = tmp_arg.lib_args[0]['type'] 389 @cast = tmp_arg.lib_cast 390 end 391 392 def lib_type_to_str 393 Arg.type_to_str(@lib_type) 394 end 395 396 def arena_item_type 397 type = Marshal.load(Marshal.dump(@lib_type)) 398 type['ptr_depth'] -= 1 399 tmp_arg = Arg.new({ 'name' => '', 'type' => type }) 400 Arg.type_to_str(tmp_arg.lib_args[0]['type']) 401 end 402 403 def call_cast 404 if @cast 405 @cast['call_cast'] 406 else 407 '' 408 end 409 end 410 411 def constructor_cast 412 if @cast 413 @cast['constructor_cast'] 414 else 415 '' 416 end 417 end 418 419 def cast 420 if @cast 421 @cast['reverse_cast'] 422 else 423 '' 424 end 425 end 426 427 def return_args_to_str 428 res = '' 429 @return_args&.map do |arg| 430 res += ', ' + Arg.arg_to_str(arg) 431 end 432 res 433 end 434 435 attr_reader :raw_type 436 attr_reader :lib_type 437 end 438 439 class ClassData < SimpleDelegator 440 def class_name 441 Es2pandaLibApi.classes.find { |_name, data| data == self }[0] 442 end 443 444 def call_cast 445 name = class_name 446 class_type = Type.new(OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 })) 447 class_type.call_cast 448 end 449 450 def constructor_type 451 name = class_name 452 Type.new(OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 })) 453 end 454 455 def constructor_cast 456 name = class_name 457 class_type = Type.new(OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 })) 458 class_type.constructor_cast 459 end 460 461 def updater_allowed 462 name = class_name 463 class_arg = Arg.new(OpenStruct.new({ 'name' => 'empty', 464 'type' => OpenStruct.new({ 'name' => name, 'ptr_depth' => 1 }) })) 465 class_arg.updater_allowed 466 end 467 468 def usings_map 469 dig(:usings)&.map { |using| [using['name'], using['type']] }.to_h || {} 470 end 471 472 def replace_with_usings(type, usings) 473 if usings[type&.name] 474 new_type = usings[type.name] 475 new_type['ref_depth'] = type['ref_depth'] || 0 476 new_type['ptr_depth'] = type['ptr_depth'] || 0 477 return new_type 478 end 479 type 480 end 481 482 def error_catch_log(mode, function, err) 483 if mode == 'constructor' 484 Es2pandaLibApi.log('error', "Error: '#{err.message}'\nConstructor: #{function.name}\nRaw:\n---\n"\ 485 "#{function.raw_declaration}\n---\n\n") 486 Es2pandaLibApi.log('backtrace', err.backtrace.join("\n"), "\n") 487 Es2pandaLibApi.stat_add_unsupported_type(err.message) if err.message.include?('Unsupported type') 488 Es2pandaLibApi.stat_add_constructor(0) 489 Es2pandaLibApi.stat_add_class(0, function.name) 490 elsif mode == 'method' 491 Es2pandaLibApi.log('error', "Error: '#{err.message}'\nClass: #{function.name}\nRaw:\n---\n"\ 492 "#{function.raw_declaration}\n---\n\n") 493 Es2pandaLibApi.log('backtrace', err.backtrace.join("\n"), "\n\n") 494 Es2pandaLibApi.stat_add_unsupported_type(err.message) if err.message.include?('Unsupported type') 495 Es2pandaLibApi.stat_add_method(0) 496 else 497 raise 'Unreachable' 498 end 499 end 500 501 def class_constructors 502 res = [] 503 usings = usings_map 504 dig(:constructors)&.each do |constructor| 505 if check_no_gen_constructor(constructor) 506 args = [] 507 begin 508 constructor_cast 509 constructor.args&.each do |arg| 510 arg['type'] = replace_with_usings(arg['type'], usings) 511 arg['type']['current_class'] = constructor.name 512 args << Arg.new(arg) 513 end 514 rescue StandardError => e 515 error_catch_log('constructor', constructor, e) 516 else 517 Es2pandaLibApi.stat_add_constructor(1) 518 Es2pandaLibApi.stat_add_class(1, class_name) 519 520 Es2pandaLibApi.log('info', "Supported constructor for class '#{class_name}'\n") 521 522 res << { 'args' => args, 'raw_decl' => constructor.raw_declaration } 523 end 524 else 525 Es2pandaLibApi.log('info', "Banned constructor for class '#{class_name}'\n") 526 end 527 end 528 res 529 end 530 531 def check_no_gen_constructor(constructor) 532 res = false 533 Es2pandaLibApi.no_gen_constructor_info['postfix_contains']&.each do |postfix| 534 res ||= constructor.postfix&.include?(postfix) 535 end 536 Es2pandaLibApi.no_gen_constructor_info['name_starts_with']&.each do |name_starts_with| 537 res ||= constructor.name&.start_with?(name_starts_with) 538 end 539 Es2pandaLibApi.no_gen_constructor_info['arg_type']&.each do |arg_type| 540 constructor.args&.each do |arg| 541 res ||= Es2pandaLibApi.check_fit(arg.type, arg_type) 542 end 543 end 544 Es2pandaLibApi.no_gen_constructor_info['call_class']&.each do |call_class| 545 res ||= (call_class['name'] == class_name) 546 end 547 !res 548 end 549 550 def check_no_gen_method(method) 551 res = false # = Will be generated 552 Es2pandaLibApi.no_gen_method_info['postfix_contains']&.each do |postfix| 553 res ||= method.postfix&.include?(postfix) 554 end 555 Es2pandaLibApi.no_gen_method_info['name_starts_with']&.each do |name_starts_with| 556 res ||= method.name&.start_with?(name_starts_with) 557 end 558 Es2pandaLibApi.no_gen_method_info['arg_type']&.each do |arg_type| 559 method.args&.each do |arg| 560 res ||= Es2pandaLibApi.check_fit(arg.type, arg_type) 561 end 562 end 563 Es2pandaLibApi.no_gen_method_info['return_type']&.each do |return_type| 564 res ||= begin 565 method.return_type['name'] == return_type['name'] && (!return_type.respond_to?('namespace') || 566 return_type['namespace'] == method.return_type['namespace']) 567 end 568 end 569 Es2pandaLibApi.no_gen_method_info['call_class']&.each do |call_class| 570 res ||= (call_class['name'] == class_name) 571 end 572 !res 573 end 574 575 def get_return_expr(return_type, call_cast, const, method, args) 576 return_expr = '' 577 578 if return_type.raw_type['name'] != 'void' && return_type.raw_type['name'] != 'ArenaVector' && 579 return_type.raw_type['name'] != 'ArenaSet' && return_type.raw_type['name'] != 'pair' && 580 return_type.raw_type['name'] != 'vector' 581 return_expr += 'auto res = ' 582 end 583 584 return_expr += return_type.cast['start']&.gsub('?const?', const) if return_type.cast 585 586 return_expr += '(' + call_cast['start']&.gsub('?const?', const) + method['name'] + '(' 587 return_expr += args&.map do |arg| 588 arg.lib_cast['var_name'] if arg.lib_cast 589 end&.join(', ') 590 return_expr += ')' + (call_cast['end']&.gsub('?const?', const) || '') + ')' 591 592 return_expr += return_type.cast['end'] || '' if return_type.cast 593 594 return_expr += if return_type.raw_type['name'] == 'void' 595 ';' 596 elsif const == '' 597 ";\n\treturn res;" 598 else 599 ";\n\treturn const_cast<const " + return_type.lib_type_to_str + '>(res);' 600 end 601 return_expr 602 end 603 604 def get_new_method_name(function_overload, name, const) 605 function_name = name + (const != '' ? 'Const' : '') 606 overload_name = function_name 607 608 if function_overload.include?(function_name) 609 overload_name += function_overload[function_name].to_s 610 function_overload[function_name] += 1 611 else 612 function_overload[function_name] = 1 613 end 614 overload_name 615 end 616 617 def get_const_modifier(return_type) 618 if return_type&.raw_type&.prefix&.include?('const') && return_type&.lib_type&.respond_to?('ptr_depth') && 619 return_type&.lib_type&.ptr_depth&.positive? 620 'const' 621 else 622 '' 623 end 624 end 625 626 def class_methods 627 res = [] 628 function_overload = {} 629 usings = usings_map 630 dig(:methods)&.each do |method| 631 if check_no_gen_method(method) 632 begin 633 return_type = Type.new(replace_with_usings(method.return_type, usings)) 634 const = get_const_modifier(return_type) 635 636 args = [] 637 method.args&.each do |arg| 638 arg['type'] = replace_with_usings(arg['type'], usings) 639 args << Arg.new(arg) 640 end 641 642 return_expr = get_return_expr(return_type, call_cast, const, method, args) 643 rescue StandardError => e 644 error_catch_log('method', method, e) 645 else 646 Es2pandaLibApi.stat_add_method(1) 647 Es2pandaLibApi.log('info', 'supported method: ', method.name, ' class: ', class_name, "\n") 648 649 res << { 'name' => method.name, 'const' => const, 'return_arg_to_str' => return_type.return_args_to_str, 650 'overload_name' => get_new_method_name(function_overload, method.name, const), 'args' => args, 651 'return_type' => return_type, 'return_expr' => return_expr, 'raw_decl' => method.raw_declaration } 652 end 653 else 654 Es2pandaLibApi.log('info', "Banned method\n") 655 end 656 end 657 res 658 end 659 end 660 661 @ast_nodes = Set.new(%w[AstNode Expression Statement TypeNode]) 662 @ast_types = Set.new(['Type']) 663 @scopes = Set.new(['Scope']) 664 @classes = {} 665 @includes = Set.new 666 @change_types = [] 667 @enums = Set.new(['AstNodeType']) 668 669 @all_methods = 0.0 670 @all_constructors = 0.0 671 @all_classes = Set.new 672 @classes_with_supported_constructor = Set.new 673 @unsupported_types = {} 674 675 @supported_methods = 0.0 676 @supported_constructors = 0.0 677 678 def log(debug_level, *args) 679 info_log = false 680 debug_log = false 681 error_log = true 682 backtrace_log = true 683 stat_log = true 684 685 if debug_level == 'info' && info_log 686 print args.join('').to_s 687 elsif debug_level == 'debug' && debug_log 688 print args.join('').to_s 689 elsif debug_level == 'error' && error_log 690 print args.join('').to_s 691 elsif debug_level == 'backtrace' && backtrace_log 692 print args.join('').to_s 693 elsif debug_level == 'stat' && stat_log 694 print args.join('').to_s 695 end 696 end 697 698 def no_gen_constructor_info 699 { 'name_starts_with' => 700 %w[AstNode ClassElement TypedStatement Annotated Scope Type], 701 'postfix_contains' => 702 ['= delete', 'override'], 703 'arg_type' => 704 [{ 'name' => 'Tag' }, 705 { 'name' => 'Number', 'namespace' => 'lexer' }, 706 { 'name' => 'Property', 'namespace' => 'AstDumper' }, 707 { 'name' => 'TSChecker', 'namespace' => 'checker' }, 708 { 'name' => 'ArenaVector', 'template_args' => [{ 'type' => { 'name' => 'pair' } }] }, 709 { 'name' => 'initializer_list' }], 710 'call_class' => 711 [{ 'name' => 'AstNode' }, { 'name' => 'ClassElement' }, { 'name' => 'TypedStatement' }, { 'name' => 'Annotated' }, 712 { 'name' => 'Scope' }, { 'name' => 'Type' }] } 713 end 714 715 def no_gen_method_info 716 { 'name_starts_with' => 717 ['~', 'HasFloatingPoint', 'AddChildLambda', 'operator=', 'NumericConditionalCheck', 'CompileComputed'], 718 'postfix_contains' => 719 ['= delete', 'override'], 720 'return_type' => 721 [{ 'name' => 'ETSChecker' }, { 'name' => 'ArenaAllocator' }, 722 { 'name' => 'Allocator' }, { 'name' => 'Program' }, { 'name' => 'Tag' }, 723 { 'name' => 'Number', 'namespace' => 'lexer' }, { 'name' => 'Property', 'namespace' => 'AstDumper' }, 724 { 'name' => 'TSChecker', 'namespace' => 'checker' }], 725 'arg_type' => 726 [{ 'name' => 'Tag' }, { 'name' => 'Number', 'namespace' => 'lexer' }, 727 { 'name' => 'Property', 'namespace' => 'AstDumper' }, { 'name' => 'TSChecker', 'namespace' => 'checker' }, 728 { 'name' => 'ArenaVector', 'template_args' => [{ 'type' => { 'name' => 'pair' } }] }, 729 { 'name' => 'initializer_list' }], 730 'call_class' => 731 [{ 'name' => 'Annotated' }] } 732 end 733 734 def check_fit(data, pattern) 735 return true if data.nil? || pattern.nil? 736 737 if pattern.is_a?(OpenStruct) || pattern.is_a?(Hash) 738 pattern.each_pair do |key, value| 739 return false unless check_fit(data[key], value) 740 end 741 elsif pattern.is_a?(Array) 742 pattern.each_with_index do |value, i| 743 return false unless check_fit(data[i], value) 744 end 745 elsif pattern.is_a?(String) || pattern.is_a?(Integer) 746 return false if data != pattern 747 end 748 749 true 750 end 751 752 def stat_add_method(support) 753 @all_methods += 1 754 @supported_methods += support 755 end 756 757 def stat_add_constructor(support) 758 @all_constructors += 1 759 @supported_constructors += support 760 end 761 762 def stat_add_class(support, class_name) 763 @all_classes << class_name 764 @classes_with_supported_constructor << class_name if support != 0 765 end 766 767 def stat_add_unsupported_type(err_msg) 768 @unsupported_types[err_msg] ||= 0 769 @unsupported_types[err_msg] += 1 770 end 771 772 def print_stats 773 Es2pandaLibApi.log('stat', "--------------\n") 774 Es2pandaLibApi.log('stat', 'Supported methods: ', @supported_methods, ' / ', @all_methods, ' ( ', 775 @supported_methods / @all_methods * 100, " % )\n") 776 Es2pandaLibApi.log('stat', 'Supported constructors: ', @supported_constructors, ' / ', @all_constructors, ' ( ', 777 @supported_constructors / @all_constructors * 100, " % )\n") 778 Es2pandaLibApi.log('stat', "Classes with supported constructor: #{@classes_with_supported_constructor.size} / "\ 779 "#{@all_classes.size} ( #{@classes_with_supported_constructor.size.to_f / @all_classes.size * 100} % )\n") 780 Es2pandaLibApi.log('stat', "--------------\n") 781 782 return if @unsupported_types.empty? 783 784 Es2pandaLibApi.log('stat', "Unsupported types for constructor: \n") 785 sorted_items = @unsupported_types.sort_by { |_key, value| -value } 786 sorted_items.each do |key, value| 787 Es2pandaLibApi.log('stat', "#{key}: #{value}\n") 788 end 789 end 790 791 def ast_nodes 792 @ast_nodes 793 end 794 795 def ast_types 796 @ast_types 797 end 798 799 def scopes 800 @scopes 801 end 802 803 def ast_variables 804 [%w[NO Variable], 805 %w[LOCAL LocalVariable], 806 %w[GLOBAL GlobalVariable], 807 %w[MODULE ModuleVariable], 808 %w[ENUM EnumVariable]] 809 end 810 811 def ast_node_additional_children 812 %w[ 813 TypedStatement 814 ClassElement 815 AnnotatedExpression 816 Literal 817 LoopStatement 818 MaybeOptionalExpression 819 Property 820 ] 821 end 822 823 def code_gen_children 824 %w[ 825 CodeGen 826 PandaGen 827 ETSGen 828 ] 829 end 830 831 def classes 832 @classes 833 end 834 835 def includes 836 @includes 837 end 838 839 def change_types 840 @change_types 841 end 842 843 def enums 844 @enums 845 end 846 847 def wrap_data(data) 848 return unless data 849 850 data.macros&.each do |macros| 851 case macros.name 852 when 'AST_NODE_MAPPING' 853 @ast_nodes.merge(Set.new(macros.values&.map { |x| x[1] })) 854 when 'AST_NODE_REINTERPRET_MAPPING' 855 @ast_nodes.merge(Set.new(macros.values&.map { |x| x[2..3] }&.flatten)) 856 when 'TYPE_MAPPING' 857 @ast_types.merge(Set.new(macros.values&.map { |x| x[1] }&.flatten)) 858 end 859 end 860 861 data.varbinder&.macros&.each do |macros| 862 case macros.name 863 when 'SCOPE_TYPES' 864 @scopes.merge(Set.new(macros.values&.map { |x| x[1] })) 865 end 866 end 867 868 data.ast_node_reinterpret_mapping&.each do |mapping| 869 @ast_nodes << mapping[2] 870 @ast_nodes << mapping[3] 871 end 872 873 data.paths&.each do |include| 874 @includes << include 875 end 876 877 data.change_types&.each do |change_type| 878 @change_types << change_type 879 end 880 881 data['ir']&.class_definitions&.each do |class_definition| 882 @classes[class_definition.name] = ClassData.new(class_definition&.public) 883 end 884 885 data.enums&.each do |enum| 886 @enums << enum.name 887 end 888 889 Enums.wrap_data(data) 890 end 891 892 module_function :wrap_data, :classes, :ast_nodes, :includes, :change_types, :enums, :ast_types, 893 :stat_add_constructor, :stat_add_method, :print_stats, :no_gen_method_info, :no_gen_constructor_info, 894 :stat_add_class, :stat_add_unsupported_type, :ast_node_additional_children, :scopes, :ast_variables, 895 :code_gen_children, :check_fit, :log 896end 897 898def Gen.on_require(data) 899 Es2pandaLibApi.wrap_data(data) 900end 901