• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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