util.rb (4477B)
1 # frozen_string_literal: true 2 3 require 'set' 4 5 module Util 6 extend self 7 8 def print_fancy fun, str, c 9 to_print = String.new 10 if c 11 loc = c.location 12 to_print = "#{loc.file}:#{loc.line}:#{c.spelling}: " 13 end 14 to_print << (str.respond_to?(:full_message) ? str.full_message : str) 15 CI.send fun, to_print 16 end 17 18 @success = true 19 def success? = @success 20 def fail!; @success = false; end 21 def print_error str, c = nil 22 @success = false 23 print_fancy :eerror, str, c 24 end 25 26 def print_warning str, c = nil 27 print_fancy :ewarn, str, c 28 end 29 30 def remove_anon_ns str 31 str.gsub '::(anonymous namespace)', '' 32 end 33 34 def fun_qualified_name c 35 res = [ c.spelling ] 36 x = c.semantic_parent 37 while x&.kind == :namespace 38 res << x.spelling 39 x = x.semantic_parent 40 end 41 res.reverse.join '::' 42 end 43 44 # get template types 45 TEMPLATE_TYPES = Set.new %i( 46 template_type_parameter non_type_template_parameter 47 template_template_parameter 48 ) 49 IGNORE_TYPES = Set.new %i( 50 parm_decl type_ref annotate_attr namespace_ref compound_stmt 51 ) 52 def collect_template_args c 53 res = [] 54 c.visit_children do |c, par| 55 kind = c.kind 56 if TEMPLATE_TYPES.member? kind 57 res << c 58 elsif !IGNORE_TYPES.member? kind 59 Util.print_warning "Unhandled arg type #{kind}", c 60 end 61 :continue 62 end 63 res 64 end 65 66 def collect_args c 67 res = [] 68 c.visit_children do |c, par| 69 res << c if c.kind == :parm_decl 70 :continue 71 end 72 res 73 end 74 75 def has_annotation? c 76 if block_given? 77 c.visit_children do |c2| 78 return true if c2.kind == :annotate_attr && yield(c2.spelling) 79 :continue 80 end 81 else 82 c.visit_children do |c2| 83 return true if c2.kind == :annotate_attr 84 :continue 85 end 86 end 87 false 88 end 89 90 def collect_annotations c 91 res = [] 92 if block_given? 93 c.visit_children do |c2| 94 if c2.kind == :annotate_attr && (mapped = yield(c2.spelling)) 95 res << mapped 96 end 97 :continue 98 end 99 else 100 c.visit_children do |c2| 101 res << c2 if c2.kind == :annotate_attr 102 :continue 103 end 104 end 105 res 106 end 107 108 def map_annotation cls, key, ann, env = {} 109 return unless ann =~ /\A#{key}{(.*)}\z/ 110 # binding fuckery: simply calling instance_eval from this function would 111 # mean that the parameters and the local variables here would end up in the 112 # scope of the annotation value eval 113 b = TOPLEVEL_BINDING.dup 114 b.local_variable_set '__eval_obj__', OpenStruct.new(env) 115 b.local_variable_set '__eval_cls__', cls 116 b.local_variable_set '__eval_str__', "__eval_cls__.new(#$1).freeze" 117 b.eval '__eval_obj__.instance_eval __eval_str__', __FILE__, __LINE__ 118 end 119 120 CPP_ESCAPES = { 121 '\\' => '\\\\', 122 "\a" => '\\a', 123 "\b" => '\\b', 124 "\t" => '\\t', 125 "\n" => '\\n', 126 "\v" => '\\v', 127 "\f" => '\\f', 128 "\r" => '\\r', 129 }.transform_keys!(&:ord).freeze 130 def cpp_inspect s 131 return s.to_s unless s.is_a? String 132 esc = s.each_byte.map do |b| 133 next CPP_ESCAPES[b] if CPP_ESCAPES[b] 134 next b.chr if (32..127) === b 135 # octal is max 3 chars, hex can be any length, so use octal 136 '\\%03o' % b 137 end.join.gsub /(\\x[0-9a-f]{2})([0-9a-f])/, '\1""\2' 138 "\"#{esc}\"" 139 end 140 end 141 142 module RecursiveClassDiscovery 143 def class_migrate_inheritable mod, ref; end 144 def get_class_info c, ann; ann; end 145 146 def class? type 147 @rec_class_cache ||= {} 148 type = type.canonical 149 name = type.spelling 150 return @rec_class_cache[name] unless @rec_class_cache[name].nil? 151 152 ann_cls = self.class.const_get :ClassAnnotation 153 ann_key = self.class.const_get :CLASS_ANNOTATION_KEY 154 ret = nil 155 tmp = nil 156 type.declaration.visit_children do |c, par| 157 case c.kind 158 when :cxx_base_specifier 159 if base_class = class?(c.type) 160 ret = true if ret.nil? 161 tmp ||= base_class.dup 162 class_migrate_inheritable tmp, base_class 163 end 164 165 when :annotate_attr 166 if at = Util.map_annotation(ann_cls, ann_key, c.spelling, c:) 167 ret = get_class_info par, at 168 end 169 end 170 :continue 171 end 172 173 ret = get_class_info type.declaration, ann_cls.new if ret == true 174 class_migrate_inheritable ret, tmp if tmp 175 @rec_class_cache[name] = ret 176 ret 177 end 178 179 def class_by_name name 180 @rec_class_cache[name] or fail "Unknown class #{name.inspect}" 181 end 182 end