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