| 1 | module c |
| 2 | |
| 3 | import v.ast |
| 4 | import v.util |
| 5 | |
| 6 | const cprefix = 'v__reflection__' |
| 7 | |
| 8 | // reflection_string maps string to its idx |
| 9 | fn (mut g Gen) reflection_string(str string) int { |
| 10 | return unsafe { |
| 11 | g.reflection_strings[str] or { |
| 12 | g.reflection_strings[str] = g.reflection_strings.len |
| 13 | g.reflection_strings.len - 1 |
| 14 | } |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | // gen_reflection_strings generates the reflectino string registration |
| 19 | @[inline] |
| 20 | fn (mut g Gen) gen_reflection_strings() { |
| 21 | for str, idx in g.reflection_strings { |
| 22 | g.writeln('\t${cprefix}add_string(_S("${str}"), ${idx});') |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | // gen_empty_array generates code for empty array |
| 27 | @[inline] |
| 28 | fn (g &Gen) gen_empty_array(type_name string) string { |
| 29 | return 'builtin____new_array_with_default(0, 0, sizeof(${type_name}), 0)' |
| 30 | } |
| 31 | |
| 32 | // gen_functionarg_array generates the code for functionarg argument |
| 33 | @[inline] |
| 34 | fn (g &Gen) gen_functionarg_array(type_name string, node ast.Fn) string { |
| 35 | if node.params.len == 0 { |
| 36 | return g.gen_empty_array(type_name) |
| 37 | } |
| 38 | mut out := 'builtin__new_array_from_c_array(${node.params.len},${node.params.len},sizeof(${type_name}),' |
| 39 | out += '_MOV((${type_name}[${node.params.len}]){' |
| 40 | out += |
| 41 | node.params.map('((${type_name}){.name=_S("${it.name}"),.typ=${int(it.typ)},.is_mut=${it.is_mut}})').join(',') |
| 42 | out += '}))' |
| 43 | return out |
| 44 | } |
| 45 | |
| 46 | // gen_functionarg_array generates the code for functionarg argument |
| 47 | @[inline] |
| 48 | fn (mut g Gen) gen_function_array(nodes []ast.Fn) string { |
| 49 | type_name := '${cprefix}Function' |
| 50 | |
| 51 | if nodes.len == 0 { |
| 52 | return g.gen_empty_array(type_name) |
| 53 | } |
| 54 | |
| 55 | mut out := 'builtin__new_array_from_c_array(${nodes.len},${nodes.len},sizeof(${type_name}),' |
| 56 | out += '_MOV((${type_name}[${nodes.len}]){' |
| 57 | out += nodes.map(g.gen_reflection_fn(it)).join(',') |
| 58 | out += '}))' |
| 59 | return out |
| 60 | } |
| 61 | |
| 62 | // gen_reflection_fn generates C code for Function struct |
| 63 | @[inline] |
| 64 | fn (mut g Gen) gen_reflection_fn(node ast.Fn) string { |
| 65 | mut arg_str := '((${cprefix}Function){' |
| 66 | v_name := node.name.all_after_last('.') |
| 67 | arg_str += '.mod_name=_S("${node.mod}"),' |
| 68 | arg_str += '.name=_S("${v_name}"),' |
| 69 | arg_str += '.args=${g.gen_functionarg_array(cprefix + 'FunctionArg', node)},' |
| 70 | arg_str += '.file_idx=${g.reflection_string(util.cescaped_path(node.file))},' |
| 71 | arg_str += '.line_start=${node.pos.line_nr},' |
| 72 | arg_str += '.line_end=${node.pos.last_line},' |
| 73 | arg_str += '.is_variadic=${node.is_variadic},' |
| 74 | arg_str += '.return_typ=${int(node.return_type)},' |
| 75 | arg_str += '.receiver_typ=${int(node.receiver_type)},' |
| 76 | arg_str += '.is_pub=${node.is_pub},' |
| 77 | arg_str += '.attrs=${g.gen_attrs_array(node.attrs)}' |
| 78 | arg_str += '})' |
| 79 | return arg_str |
| 80 | } |
| 81 | |
| 82 | // gen_reflection_sym generates C code for TypeSymbol struct |
| 83 | @[inline] |
| 84 | fn (mut g Gen) gen_reflection_sym(tsym ast.TypeSymbol) string { |
| 85 | kind_name := tsym.kind.str() |
| 86 | name := tsym.name.all_after_last('.') |
| 87 | info := g.gen_reflection_sym_info(tsym) |
| 88 | methods := g.gen_function_array(tsym.methods) |
| 89 | return '(${cprefix}TypeSymbol){.name=_S("${name}"),.mod=_S("${tsym.mod}"),.idx=${tsym.idx},.parent_idx=${tsym.parent_idx},.language=${cprefix}VLanguage__${tsym.language},.kind=${cprefix}VKind__${kind_name},.info=${info},.methods=${methods}}' |
| 90 | } |
| 91 | |
| 92 | // gen_attrs_array generates C code for []VAttribute |
| 93 | @[inline] |
| 94 | fn (g &Gen) gen_attrs_array(attrs []ast.Attr) string { |
| 95 | type_name := 'VAttribute' |
| 96 | if attrs.len == 0 { |
| 97 | return g.gen_empty_array(type_name) |
| 98 | } |
| 99 | mut items := []string{cap: attrs.len} |
| 100 | for attr in attrs { |
| 101 | items << '((${type_name}){.name=_S("${cescape_nonascii(util.smart_quote(attr.name, false))}"),.has_arg=${attr.has_arg},.arg=_S("${cescape_nonascii(util.smart_quote(attr.arg, |
| 102 | false))}"),.kind=${int(attr.kind)}})' |
| 103 | } |
| 104 | mut out := 'builtin__new_array_from_c_array(${attrs.len},${attrs.len},sizeof(${type_name}),' |
| 105 | out += '_MOV((${type_name}[${attrs.len}]){' |
| 106 | out += items.join(',') |
| 107 | out += '}))' |
| 108 | return out |
| 109 | } |
| 110 | |
| 111 | // gen_fields_array generates C code for []StructField |
| 112 | @[inline] |
| 113 | fn (g &Gen) gen_fields_array(fields []ast.StructField) string { |
| 114 | if fields.len == 0 { |
| 115 | return g.gen_empty_array('${cprefix}StructField') |
| 116 | } |
| 117 | mut out := 'builtin__new_array_from_c_array(${fields.len},${fields.len},sizeof(${cprefix}StructField),' |
| 118 | out += '_MOV((${cprefix}StructField[${fields.len}]){' |
| 119 | out += |
| 120 | fields.map('((${cprefix}StructField){.name=_S("${it.name}"),.typ=${int(it.typ)},.attrs=${g.gen_attrs_array(it.attrs)},.is_pub=${it.is_pub},.is_mut=${it.is_mut}})').join(',') |
| 121 | out += '}))' |
| 122 | return out |
| 123 | } |
| 124 | |
| 125 | // gen_type_array generates C code for []Type |
| 126 | @[inline] |
| 127 | fn (g &Gen) gen_type_array(types []ast.Type) string { |
| 128 | if types.len == 0 { |
| 129 | return g.gen_empty_array(ast.int_type_name) |
| 130 | } |
| 131 | return 'builtin__new_array_from_c_array(${types.len},${types.len},sizeof(int),_MOV((int[${types.len}]){${types.map(int(it).str()).join(',')}}))' |
| 132 | } |
| 133 | |
| 134 | // gen_string_array generates C code for []string |
| 135 | @[inline] |
| 136 | fn (g &Gen) gen_string_array(strs []string) string { |
| 137 | if strs.len == 0 { |
| 138 | return g.gen_empty_array('string') |
| 139 | } |
| 140 | items := strs.map('_S("${it}")').join(',') |
| 141 | return 'builtin__new_array_from_c_array(${strs.len},${strs.len},sizeof(string),_MOV((string[${strs.len}]){${items}}))' |
| 142 | } |
| 143 | |
| 144 | // gen_reflection_sym_info generates C code for TypeSymbol's info sum type |
| 145 | @[inline] |
| 146 | fn (mut g Gen) gen_reflection_sym_info(tsym ast.TypeSymbol) string { |
| 147 | match tsym.kind { |
| 148 | .array { |
| 149 | info := tsym.info as ast.Array |
| 150 | s := 'ADDR(${cprefix}Array,(((${cprefix}Array){.nr_dims=${info.nr_dims},.elem_type=${int(info.elem_type)}})))' |
| 151 | return '(${cprefix}TypeInfo){._${cprefix}Array = builtin__memdup(${s},sizeof(${cprefix}Array)),._typ=${g.table.find_type_idx('v.reflection.Array')}}' |
| 152 | } |
| 153 | .array_fixed { |
| 154 | info := tsym.info as ast.ArrayFixed |
| 155 | s := 'ADDR(${cprefix}ArrayFixed,(((${cprefix}ArrayFixed){.size=${info.size},.elem_type=${int(info.elem_type)}})))' |
| 156 | return '(${cprefix}TypeInfo){._${cprefix}ArrayFixed=builtin__memdup(${s},sizeof(${cprefix}ArrayFixed)),._typ=${g.table.find_type_idx('v.reflection.ArrayFixed')}}' |
| 157 | } |
| 158 | .map { |
| 159 | info := tsym.info as ast.Map |
| 160 | s := 'ADDR(${cprefix}Map,(((${cprefix}Map){.key_type=${int(info.key_type)},.value_type=${int(info.value_type)}})))' |
| 161 | return '(${cprefix}TypeInfo){._${cprefix}Map=builtin__memdup(${s},sizeof(${cprefix}Map)),._typ=${g.table.find_type_idx('v.reflection.Map')}}' |
| 162 | } |
| 163 | .sum_type { |
| 164 | info := tsym.info as ast.SumType |
| 165 | s := 'ADDR(${cprefix}SumType,(((${cprefix}SumType){.parent_idx=${info.parent_type.idx()},.variants=${g.gen_type_array(info.variants)}})))' |
| 166 | return '(${cprefix}TypeInfo){._${cprefix}SumType=builtin__memdup(${s},sizeof(${cprefix}SumType)),._typ=${g.table.find_type_idx('v.reflection.SumType')}}' |
| 167 | } |
| 168 | .struct { |
| 169 | info := tsym.info as ast.Struct |
| 170 | attrs := g.gen_attrs_array(info.attrs) |
| 171 | fields := g.gen_fields_array(info.fields) |
| 172 | s := 'ADDR(${cprefix}Struct,(((${cprefix}Struct){.parent_idx=${(tsym.info as ast.Struct).parent_type.idx()},.attrs=${attrs},.fields=${fields}})))' |
| 173 | return '(${cprefix}TypeInfo){._${cprefix}Struct=builtin__memdup(${s},sizeof(${cprefix}Struct)),._typ=${g.table.find_type_idx('v.reflection.Struct')}}' |
| 174 | } |
| 175 | .enum { |
| 176 | info := tsym.info as ast.Enum |
| 177 | vals := g.gen_string_array(info.vals) |
| 178 | s := 'ADDR(${cprefix}Enum,(((${cprefix}Enum){.vals=${vals},.is_flag=${info.is_flag}})))' |
| 179 | return '(${cprefix}TypeInfo){._${cprefix}Enum=builtin__memdup(${s},sizeof(${cprefix}Enum)),._typ=${g.table.find_type_idx('v.reflection.Enum')}}' |
| 180 | } |
| 181 | .function { |
| 182 | info := tsym.info as ast.FnType |
| 183 | s := 'ADDR(${cprefix}Function,${g.gen_reflection_fn(info.func)})' |
| 184 | return '(${cprefix}TypeInfo){._${cprefix}Function=builtin__memdup(${s},sizeof(${cprefix}Function)),._typ=${g.table.find_type_idx('v.reflection.Function')}}' |
| 185 | } |
| 186 | .interface { |
| 187 | name := tsym.name.all_after_last('.') |
| 188 | info := tsym.info as ast.Interface |
| 189 | methods := g.gen_function_array(info.methods) |
| 190 | fields := g.gen_fields_array(info.fields) |
| 191 | s := 'ADDR(${cprefix}Interface,(((${cprefix}Interface){.name=_S("${name}"),.methods=${methods},.fields=${fields},.is_generic=${info.is_generic}})))' |
| 192 | return '(${cprefix}TypeInfo){._${cprefix}Interface=builtin__memdup(${s},sizeof(${cprefix}Interface)),._typ=${g.table.find_type_idx('v.reflection.Interface')}}' |
| 193 | } |
| 194 | .alias { |
| 195 | info := tsym.info as ast.Alias |
| 196 | s := 'ADDR(${cprefix}Alias,(((${cprefix}Alias){.parent_idx=${info.parent_type.idx()},.language=${cprefix}VLanguage__${info.language.str()}})))' |
| 197 | return '(${cprefix}TypeInfo){._${cprefix}Alias=builtin__memdup(${s},sizeof(${cprefix}Alias)),._typ=${g.table.find_type_idx('v.reflection.Alias')}}' |
| 198 | } |
| 199 | .multi_return { |
| 200 | info := tsym.info as ast.MultiReturn |
| 201 | s := 'ADDR(${cprefix}MultiReturn,(((${cprefix}MultiReturn){.types=${g.gen_type_array(info.types)}})))' |
| 202 | return '(${cprefix}TypeInfo){._${cprefix}MultiReturn=builtin__memdup(${s},sizeof(${cprefix}MultiReturn)),._typ=${g.table.find_type_idx('v.reflection.MultiReturn')}}' |
| 203 | } |
| 204 | else { |
| 205 | s := 'ADDR(${cprefix}None,(((${cprefix}None){.parent_idx=${tsym.parent_idx},})))' |
| 206 | return '(${cprefix}TypeInfo){._${cprefix}None=builtin__memdup(${s},sizeof(${cprefix}None)),._typ=${g.table.find_type_idx('v.reflection.None')}}' |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | // gen_reflection_data generates code to initialized V reflection metadata |
| 212 | fn (mut g Gen) gen_reflection_data() { |
| 213 | // modules declaration |
| 214 | for mod_name in g.table.modules { |
| 215 | g.writeln('\t${cprefix}add_module(_S("${mod_name}"));') |
| 216 | } |
| 217 | |
| 218 | // type symbols declaration |
| 219 | for _, tsym in g.table.type_symbols { |
| 220 | sym := g.gen_reflection_sym(tsym) |
| 221 | g.writeln('\t${cprefix}add_type_symbol(${sym});') |
| 222 | } |
| 223 | |
| 224 | // types declaration |
| 225 | for full_name, idx in g.table.type_idxs { |
| 226 | name := full_name.all_after_last('.') |
| 227 | g.writeln('\t${cprefix}add_type((${cprefix}Type){.name=_S("${name}"),.idx=${idx}});') |
| 228 | } |
| 229 | |
| 230 | // func declaration (methods come from struct methods) |
| 231 | for _, fn_ in g.table.fns { |
| 232 | if fn_.no_body || fn_.is_method || fn_.language != .v { |
| 233 | continue |
| 234 | } |
| 235 | func := g.gen_reflection_fn(fn_) |
| 236 | g.writeln('\t${cprefix}add_func(${func});') |
| 237 | } |
| 238 | |
| 239 | g.gen_reflection_strings() |
| 240 | } |
| 241 | |