diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 97b0b80639..a1f035e80a 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -237,10 +237,7 @@ mut: out_fn_start_pos []int // for generating multiple .c files, stores locations of all fn positions in `out` string builder static_modifier string // for parallel_cc - has_reflection bool - // reflection metadata initialization - reflection_funcs strings.Builder - reflection_others strings.Builder + has_reflection bool reflection_strings &map[string]int } @@ -314,8 +311,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref_ &pref.Preferences) (string || pref_.os in [.wasm32, .wasm32_emscripten]) static_modifier: if pref_.parallel_cc { 'static' } else { '' } has_reflection: 'v.reflection' in table.modules - reflection_funcs: strings.new_builder(100) - reflection_others: strings.new_builder(100) reflection_strings: &reflection_strings } @@ -370,7 +365,6 @@ pub fn gen(files []&ast.File, table &ast.Table, pref_ &pref.Preferences) (string global_g.embedded_data.write(g.embedded_data) or { panic(err) } global_g.shared_types.write(g.shared_types) or { panic(err) } global_g.shared_functions.write(g.channel_definitions) or { panic(err) } - global_g.reflection_funcs.write(g.reflection_funcs) or { panic(err) } global_g.force_main_console = global_g.force_main_console || g.force_main_console @@ -679,8 +673,6 @@ fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) &Gen { is_cc_msvc: global_g.is_cc_msvc use_segfault_handler: global_g.use_segfault_handler has_reflection: 'v.reflection' in global_g.table.modules - reflection_funcs: strings.new_builder(100) - reflection_others: strings.new_builder(100) reflection_strings: global_g.reflection_strings } g.gen_file() diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index c4369bd5c0..3ec4d96bfb 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -288,7 +288,6 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { } fn_header := '${visibility_kw}${type_name} ${fn_attrs}${name}(' g.definitions.write_string(fn_header) - g.gen_reflection_function(node) g.write(fn_header) } arg_start_pos := g.out.len diff --git a/vlib/v/gen/c/reflection.v b/vlib/v/gen/c/reflection.v index ad8ba6a77e..ec48f121cb 100644 --- a/vlib/v/gen/c/reflection.v +++ b/vlib/v/gen/c/reflection.v @@ -3,6 +3,8 @@ module c import v.ast import v.util +const cprefix = 'v__reflection__' + // reflection_string maps string to its idx fn (mut g Gen) reflection_string(str string) int { return unsafe { @@ -17,7 +19,7 @@ fn (mut g Gen) reflection_string(str string) int { [inline] fn (mut g Gen) gen_reflection_strings() { for str, idx in g.reflection_strings { - g.reflection_others.write_string('\tv__reflection__add_string(_SLIT("${str}"), ${idx});\n') + g.writeln('\t${c.cprefix}add_string(_SLIT("${str}"), ${idx});') } } @@ -29,23 +31,21 @@ fn (g Gen) gen_empty_array(type_name string) string { // gen_functionarg_array generates the code for functionarg argument [inline] -fn (g Gen) gen_functionarg_array(type_name string, node ast.FnDecl) string { +fn (g Gen) gen_functionarg_array(type_name string, node ast.Fn) string { if node.params.len == 0 { return g.gen_empty_array(type_name) } mut out := 'new_array_from_c_array(${node.params.len},${node.params.len},sizeof(${type_name}),' out += '_MOV((${type_name}[${node.params.len}]){' - for param in node.params { - out += '((${type_name}){.name=_SLIT("${param.name}"),.typ=${param.typ.idx()},}),' - } + out += node.params.map('((${type_name}){.name=_SLIT("${it.name}"),.typ=${int(it.typ)},.is_mut=${it.is_mut}})').join(',') out += '}))' return out } // gen_functionarg_array generates the code for functionarg argument [inline] -fn (mut g Gen) gen_function_array(nodes []ast.FnDecl) string { - type_name := 'v__reflection__Function' +fn (mut g Gen) gen_function_array(nodes []ast.Fn) string { + type_name := '${c.cprefix}Function' if nodes.len == 0 { return g.gen_empty_array(type_name) @@ -53,108 +53,182 @@ fn (mut g Gen) gen_function_array(nodes []ast.FnDecl) string { mut out := 'new_array_from_c_array(${nodes.len},${nodes.len},sizeof(${type_name}),' out += '_MOV((${type_name}[${nodes.len}]){' - for method in nodes { - out += g.gen_reflection_fndecl(method) - out += ',' - } + out += nodes.map(g.gen_reflection_fn(it)).join(',') out += '}))' return out } -// gen_reflection_enum_fields generates C code for enum fields +// gen_reflection_fn generates C code for Function struct [inline] -fn (g Gen) gen_reflection_enum_fields(fields []ast.EnumField) string { - if fields.len == 0 { - return g.gen_empty_array('v__reflection__EnumField') - } - mut out := 'new_array_from_c_array(${fields.len},${fields.len},sizeof(v__reflection__EnumField),' - out += '_MOV((v__reflection__EnumField[${fields.len}]){' - for field in fields { - out += '((v__reflection__EnumField){.name=_SLIT("${field.name}")}),' - } - out += '}))' - return out -} - -// gen_reflection_fndecl generates C code for function declaration -[inline] -fn (mut g Gen) gen_reflection_fndecl(node ast.FnDecl) string { - mut arg_str := '((v__reflection__Function){' +fn (mut g Gen) gen_reflection_fn(node ast.Fn) string { + mut arg_str := '((${c.cprefix}Function){' v_name := node.name.all_after_last('.') arg_str += '.mod_name=_SLIT("${node.mod}"),' arg_str += '.name=_SLIT("${v_name}"),' - arg_str += '.args=${g.gen_functionarg_array('v__reflection__FunctionArg', node)},' + arg_str += '.args=${g.gen_functionarg_array(c.cprefix + 'FunctionArg', node)},' arg_str += '.file_idx=${g.reflection_string(util.cescaped_path(node.file))},' arg_str += '.line_start=${node.pos.line_nr},' arg_str += '.line_end=${node.pos.last_line},' arg_str += '.is_variadic=${node.is_variadic},' - arg_str += '.return_typ=${node.return_type.idx()},' - arg_str += '.receiver_typ=${node.receiver.typ.idx()}' + arg_str += '.return_typ=${int(node.return_type)},' + arg_str += '.receiver_typ=${int(node.receiver_type)},' + arg_str += '.is_pub=${node.is_pub}' arg_str += '})' return arg_str } // gen_reflection_sym generates C code for TypeSymbol struct [inline] -fn (g Gen) gen_reflection_sym(tsym ast.TypeSymbol) string { +fn (mut g Gen) gen_reflection_sym(tsym ast.TypeSymbol) string { kind_name := if tsym.kind in [.none_, .struct_, .enum_, .interface_] { tsym.kind.str() + '_' } else { tsym.kind.str() } - return '(v__reflection__TypeSymbol){.name=_SLIT("${tsym.name}"),.idx=${tsym.idx},.parent_idx=${tsym.parent_idx},.language=_SLIT("${tsym.language}"),.kind=v__ast__Kind__${kind_name}}' + info := g.gen_reflection_sym_info(tsym) + methods := g.gen_function_array(tsym.methods) + return '(${c.cprefix}TypeSymbol){.name=_SLIT("${tsym.name}"),.idx=${tsym.idx},.parent_idx=${tsym.parent_idx},.language=${c.cprefix}VLanguage__${tsym.language},.kind=${c.cprefix}VKind__${kind_name},.info=${info},.methods=${methods}}' } -// gen_reflection_function generates C code for reflection function metadata +// gen_attrs_array generates C code for []Attr [inline] -fn (mut g Gen) gen_reflection_function(node ast.FnDecl) { - if !g.has_reflection { - return +fn (g Gen) gen_attrs_array(attrs []ast.Attr) string { + if attrs.len == 0 { + return g.gen_empty_array('string') + } + mut out := 'new_array_from_c_array(${attrs.len},${attrs.len},sizeof(string),' + out += '_MOV((string[${attrs.len}]){' + out += attrs.map(if it.has_arg { '_SLIT("${it.name}=${it.arg}")' } else { '_SLIT("${it.name}")' }).join(',') + out += '}))' + return out +} + +// gen_fields_array generates C code for []StructField +[inline] +fn (g Gen) gen_fields_array(fields []ast.StructField) string { + if fields.len == 0 { + return g.gen_empty_array('${c.cprefix}StructField') + } + mut out := 'new_array_from_c_array(${fields.len},${fields.len},sizeof(${c.cprefix}StructField),' + out += '_MOV((${c.cprefix}StructField[${fields.len}]){' + out += fields.map('((${c.cprefix}StructField){.name=_SLIT("${it.name}"),.typ=${int(it.typ)},.attrs=${g.gen_attrs_array(it.attrs)},.is_pub=${it.is_pub},.is_mut=${it.is_mut}})').join(',') + out += '}))' + return out +} + +// gen_type_array generates C code for []Type +[inline] +fn (g Gen) gen_type_array(types []ast.Type) string { + if types.len == 0 { + return g.gen_empty_array('int') + } + return 'new_array_from_c_array(${types.len},${types.len},sizeof(int),_MOV((int[${types.len}]){${types.map(int(it).str()).join(',')}}))' +} + +// gen_string_array generates C code for []string +[inline] +fn (g Gen) gen_string_array(strs []string) string { + if strs.len == 0 { + return g.gen_empty_array('string') + } + items := strs.map('_SLIT("${it}")').join(',') + return 'new_array_from_c_array(${strs.len},${strs.len},sizeof(string),_MOV((string[${strs.len}]){${items}}))' +} + +// gen_reflection_sym_info generates C code for TypeSymbol's info sum type +[inline] +fn (mut g Gen) gen_reflection_sym_info(tsym ast.TypeSymbol) string { + match tsym.kind { + .array { + info := tsym.info as ast.Array + s := 'ADDR(${c.cprefix}Array,(((${c.cprefix}Array){.nr_dims=${info.nr_dims},.elem_type=${int(info.elem_type)}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}Array = memdup(${s},sizeof(${c.cprefix}Array)),._typ=${g.table.find_type_idx('v.reflection.Array')}}' + } + .array_fixed { + info := tsym.info as ast.ArrayFixed + s := 'ADDR(${c.cprefix}ArrayFixed,(((${c.cprefix}ArrayFixed){.size=${info.size},.elem_type=${int(info.elem_type)}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}ArrayFixed=memdup(${s},sizeof(${c.cprefix}ArrayFixed)),._typ=${g.table.find_type_idx('v.reflection.ArrayFixed')}}' + } + .map { + info := tsym.info as ast.Map + s := 'ADDR(${c.cprefix}Map,(((${c.cprefix}Map){.key_type=${int(info.key_type)},.value_type=${int(info.value_type)}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}Map=memdup(${s},sizeof(${c.cprefix}Map)),._typ=${g.table.find_type_idx('v.reflection.Map')}}' + } + .sum_type { + info := tsym.info as ast.SumType + s := 'ADDR(${c.cprefix}SumType,(((${c.cprefix}SumType){.parent_idx=${info.parent_type.idx()},.variants=${g.gen_type_array(info.variants)}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}SumType=memdup(${s},sizeof(${c.cprefix}SumType)),._typ=${g.table.find_type_idx('v.reflection.SumType')}}' + } + .struct_ { + info := tsym.info as ast.Struct + attrs := g.gen_attrs_array(info.attrs) + fields := g.gen_fields_array(info.fields) + s := 'ADDR(${c.cprefix}Struct,(((${c.cprefix}Struct){.parent_idx=${(tsym.info as ast.Struct).parent_type.idx()},.attrs=${attrs},.fields=${fields}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}Struct=memdup(${s},sizeof(${c.cprefix}Struct)),._typ=${g.table.find_type_idx('v.reflection.Struct')}}' + } + .enum_ { + info := tsym.info as ast.Enum + vals := g.gen_string_array(info.vals) + s := 'ADDR(${c.cprefix}Enum,(((${c.cprefix}Enum){.vals=${vals},.is_flag=${info.is_flag}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}Enum=memdup(${s},sizeof(${c.cprefix}Enum)),._typ=${g.table.find_type_idx('v.reflection.Enum')}}' + } + .function { + info := tsym.info as ast.FnType + s := 'ADDR(${c.cprefix}Function,${g.gen_reflection_fn(info.func)})' + return '(${c.cprefix}TypeInfo){._${c.cprefix}Function=memdup(${s},sizeof(${c.cprefix}Function)),._typ=${g.table.find_type_idx('v.reflection.Function')}}' + } + .interface_ { + name := tsym.name.all_after_last('.') + info := tsym.info as ast.Interface + methods := g.gen_function_array(info.methods) + fields := g.gen_fields_array(info.fields) + s := 'ADDR(${c.cprefix}Interface,(((${c.cprefix}Interface){.name=_SLIT("${name}"),.methods=${methods},.fields=${fields},.is_generic=${info.is_generic}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}Interface=memdup(${s},sizeof(${c.cprefix}Interface)),._typ=${g.table.find_type_idx('v.reflection.Interface')}}' + } + .alias { + info := tsym.info as ast.Alias + s := 'ADDR(${c.cprefix}Alias,(((${c.cprefix}Alias){.parent_idx=${info.parent_type.idx()},.language=${c.cprefix}VLanguage__${info.language.str()}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}Alias=memdup(${s},sizeof(${c.cprefix}Alias)),._typ=${g.table.find_type_idx('v.reflection.Alias')}}' + } + .multi_return { + info := tsym.info as ast.MultiReturn + s := 'ADDR(${c.cprefix}MultiReturn,(((${c.cprefix}MultiReturn){.types=${g.gen_type_array(info.types)}})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}MultiReturn=memdup(${s},sizeof(${c.cprefix}MultiReturn)),._typ=${g.table.find_type_idx('v.reflection.MultiReturn')}}' + } + else { + s := 'ADDR(${c.cprefix}None,(((${c.cprefix}None){.parent_idx=${tsym.parent_idx},})))' + return '(${c.cprefix}TypeInfo){._${c.cprefix}None=memdup(${s},sizeof(${c.cprefix}None)),._typ=${g.table.find_type_idx('v.reflection.None')}}' + } } - func_struct := g.gen_reflection_fndecl(node) - g.reflection_funcs.write_string('\tv__reflection__add_func(${func_struct});\n') } // gen_reflection_data generates code to initilized V reflection metadata fn (mut g Gen) gen_reflection_data() { // modules declaration for mod_name in g.table.modules { - g.reflection_others.write_string('\tv__reflection__add_module(_SLIT("${mod_name}"));\n') - } - - // enum declaration - for full_name, enum_ in g.table.enum_decls { - name := full_name.all_after_last('.') - fields := g.gen_reflection_enum_fields(enum_.fields) - g.reflection_others.write_string('\tv__reflection__add_enum((v__reflection__Enum){.name=_SLIT("${name}"),.is_pub=${enum_.is_pub},.is_flag=${enum_.is_flag},.typ=${enum_.typ.idx()},.line_start=${enum_.pos.line_nr},.line_end=${enum_.pos.last_line},.fields=${fields}});\n') - } - - // types declaration - for full_name, idx in g.table.type_idxs { - tsym := g.table.sym_by_idx(idx) - name := full_name.all_after_last('.') - sym := g.gen_reflection_sym(tsym) - g.reflection_others.write_string('\tv__reflection__add_type((v__reflection__Type){.name=_SLIT("${name}"),.idx=${idx},.sym=${sym}});\n') - } - - // interface declaration - for _, idecl in g.table.interfaces { - name := idecl.name.all_after_last('.') - methods := g.gen_function_array(idecl.methods) - g.reflection_others.write_string('\tv__reflection__add_interface((v__reflection__Interface){.name=_SLIT("${name}"),.typ=${idecl.typ.idx()},.is_pub=${idecl.is_pub},.methods=${methods}});\n') + g.writeln('\t${c.cprefix}add_module(_SLIT("${mod_name}"));') } // type symbols declaration for _, tsym in g.table.type_symbols { sym := g.gen_reflection_sym(tsym) - g.reflection_others.write_string('\tv__reflection__add_type_symbol(${sym});\n') + g.writeln('\t${c.cprefix}add_type_symbol(${sym});') + } + + // types declaration + for full_name, idx in g.table.type_idxs { + name := full_name.all_after_last('.') + g.writeln('\t${c.cprefix}add_type((${c.cprefix}Type){.name=_SLIT("${name}"),.idx=${idx}});') + } + + // func declaration (methods come from struct methods) + for _, fn_ in g.table.fns { + if fn_.no_body || fn_.is_method || fn_.language != .v { + continue + } + func := g.gen_reflection_fn(fn_) + g.writeln('\t${c.cprefix}add_func(${func});') } g.gen_reflection_strings() - - // funcs meta info filling - g.writeln(g.reflection_funcs.str()) - - // others meta info filling - g.writeln(g.reflection_others.str()) } diff --git a/vlib/v/reflection/reflection.v b/vlib/v/reflection/reflection.v index d3a6096da2..b057fb2b28 100644 --- a/vlib/v/reflection/reflection.v +++ b/vlib/v/reflection/reflection.v @@ -1,7 +1,7 @@ [has_globals] module reflection -import v.ast +import arrays __global g_reflection = Reflection{} @@ -17,37 +17,187 @@ pub mut: strings map[int]string } -pub struct Interface { -pub: - name string // interface name - typ int // type idx - is_pub bool // is pub? - methods []Function // methods +pub enum VLanguage { + v + c + js + amd64 // aka x86_64 + i386 + arm64 // 64-bit arm + arm32 // 32-bit arm + rv64 // 64-bit risc-v + rv32 // 32-bit risc-v + wasm32 } -pub struct EnumField { +type VType = u32 + +// max of 8 +pub enum VTypeFlag { + option + result + variadic + generic + shared_f + atomic_f +} + +pub enum VKind { + placeholder + void + voidptr + byteptr + charptr + i8 + i16 + int + i64 + isize + u8 + u16 + u32 + u64 + usize + f32 + f64 + char + rune + bool + none_ + string + array + array_fixed + map + chan + any + struct_ + generic_inst + multi_return + sum_type + alias + enum_ + function + interface_ + float_literal + int_literal + aggregate + thread +} + +// return true if `flag` is set on `t` +[inline] +pub fn (t VType) has_flag(flag VTypeFlag) bool { + return int(t) & (1 << (int(flag) + 24)) > 0 +} + +[inline] +pub fn (t VType) idx() int { + return u16(t) & 0xffff +} + +pub fn (t VType) str() string { + return 'VType(0x${t.hex()} = ${u32(t)})' +} + +// return true if `t` is a pointer (nr_muls>0) +[inline] +pub fn (t VType) is_ptr() bool { + // any normal pointer, i.e. &Type, &&Type etc; + // Note: voidptr, charptr and byteptr are NOT included! + return (int(t) >> 16) & 0xff > 0 +} + +pub struct ArrayFixed { pub: - name string // field name + size int // array size + elem_type int // elem type idx +} + +pub struct Array { +pub: + nr_dims int // nr of dimensions + elem_type int // elem type idx +} + +pub struct Alias { +pub: + parent_idx int // parent type idx + language VLanguage // language +} + +pub struct Interface { +pub: + name string // interface name + methods []Function // methods + fields []StructField // fields + is_generic bool // is generic? +} + +pub struct None { +pub: + parent_idx int } pub struct Enum { pub: - name string // enum name - is_pub bool // is pub? - is_flag bool // is flag? - typ int // type idx - line_start int // decl start line - line_end int // decl end line - fields []EnumField // enum fields + vals []string // enum values + is_flag bool // is flag? } +pub struct StructField { +pub: + name string // field name + typ VType // type + attrs []string // field attrs + is_pub bool // is pub? + is_mut bool // is mut? +} + +pub struct Struct { +pub: + parent_idx int // parent type + attrs []string // struct attrs + fields []StructField // fields +} + +pub struct SumType { +pub: + parent_idx int // parent type + variants []VType // variant type +} + +pub struct Map { +pub: + key_type VType // key type + value_type VType // value type +} + +pub struct MultiReturn { +pub: + types []VType // types +} + +pub type TypeInfo = Alias + | Array + | ArrayFixed + | Enum + | Function + | Interface + | Map + | MultiReturn + | None + | Struct + | SumType + pub struct TypeSymbol { pub: - name string // symbol name - idx int // symbol idx - parent_idx int // symbol parent idx - language string // language - kind ast.Kind // kind + name string // symbol name + idx int // symbol idx + parent_idx int // symbol parent idx + language VLanguage // language + kind VKind // kind + info TypeInfo // info + methods []Function // methods } pub struct Type { @@ -64,8 +214,9 @@ pub: pub struct FunctionArg { pub: - name string // argument name - typ int // argument type idx + name string // argument name + typ VType // argument type idx + is_mut bool // is mut? } pub struct Function { @@ -73,12 +224,13 @@ pub: mod_name string // module name name string // function/method name args []FunctionArg // function/method args - file_idx int // source file name - line_start int // decl start line - line_end int // decl end line - is_variadic bool // is variadic? - return_typ int // return type idx - receiver_typ int // receiver type idx (is a method) + file_idx int // source file name + line_start int // decl start line + line_end int // decl end line + is_variadic bool // is variadic? + return_typ VType // return type idx + receiver_typ VType // receiver type idx (is a method) + is_pub bool // is pub? } // API module @@ -99,7 +251,14 @@ pub fn get_modules() []Module { // get_functions returns the functions built with V source pub fn get_funcs() []Function { - return g_reflection.funcs + mut out := g_reflection.funcs.clone() + out << arrays.flatten[Function](get_types().map(it.sym.methods).filter(it.len)) + return out +} + +pub fn get_structs() []Type { + struct_idxs := g_reflection.type_symbols.filter(it.kind == .struct_).map(it.idx) + return g_reflection.types.filter(it.idx in struct_idxs) } // get_types returns the registered types @@ -108,8 +267,9 @@ pub fn get_types() []Type { } // get_enums returns the registered enums -pub fn get_enums() []Enum { - return g_reflection.enums +pub fn get_enums() []Type { + enum_idxs := g_reflection.type_symbols.filter(it.kind == .enum_).map(it.idx) + return g_reflection.types.filter(it.idx in enum_idxs) } // get_aliases returns the registered aliases @@ -120,7 +280,8 @@ pub fn get_aliases() []Type { // get_interfaces returns the registered aliases pub fn get_interfaces() []Interface { - return g_reflection.interfaces + iface_idxs := g_reflection.type_symbols.filter(it.kind == .interface_).map(it.idx) + return g_reflection.types.filter(it.idx in iface_idxs).map(it.sym.info as Interface) } // get_sum_types returns the registered sum types @@ -170,7 +331,10 @@ fn add_func(func Function) { [markused] fn add_type(type_ Type) { - g_reflection.types << type_ + g_reflection.types << Type{ + ...type_ + sym: g_reflection.type_symbols.filter(it.idx == type_.idx).first() + } } [markused] @@ -178,16 +342,6 @@ fn add_type_symbol(typesymbol TypeSymbol) { g_reflection.type_symbols << typesymbol } -[markused] -fn add_enum(enum_ Enum) { - g_reflection.enums << enum_ -} - -[markused] -fn add_interface(interface_ Interface) { - g_reflection.interfaces << interface_ -} - [markused] fn add_string(str string, idx int) { g_reflection.strings[idx] = str diff --git a/vlib/v/tests/reflection_sym_test.v b/vlib/v/tests/reflection_sym_test.v new file mode 100644 index 0000000000..494c4d8c9f --- /dev/null +++ b/vlib/v/tests/reflection_sym_test.v @@ -0,0 +1,117 @@ +import v.reflection + +[test_struct] +struct Test { + m map[int]string [test] + n ?string [test2; test3] +} + +enum Flags { + foo + bar +} + +type MySum = f64 | int + +type MyAlias = int + +fn foo() ?string { + return '' +} + +fn bar() !string { + return '' +} + +fn baz() (int, f64, string) { + return 1, 2.0, 'foo' +} + +fn test_flag_option() { + funcs := reflection.get_funcs().filter(it.name == 'foo') + assert funcs[0].return_typ.has_flag(.option) +} + +fn test_flag_result() { + funcs := reflection.get_funcs().filter(it.name == 'bar') + assert funcs[0].return_typ.has_flag(.result) +} + +fn test_array_sym() { + var := [1, 2] + typ := reflection.type_of(var) + assert typ.sym.kind == .array + assert typ.sym.language == .v + assert typ.sym.methods.len > 0 + assert typ.sym.methods.filter(it.name == 'reduce').len > 0 + assert typ.sym.name == '[]int' + assert (typ.sym.info as reflection.Array).nr_dims == 1 + assert (typ.sym.info as reflection.Array).elem_type == typeof[int]().idx +} + +fn test_sumtype_sym() { + var := MySum(1) + typ := reflection.type_of(var) + assert typ.sym.kind == .sum_type + assert (typ.sym.info as reflection.SumType).variants[0] == typeof[f64]().idx + assert (typ.sym.info as reflection.SumType).variants[1] == typeof[int]().idx +} + +fn test_alias_sym() { + var := MyAlias(1) + typ := reflection.type_of(var) + assert typ.sym.kind == .alias + assert typ.sym.language == .v + assert (typ.sym.info as reflection.Alias).parent_idx == typeof[int]().idx + assert typ.sym.methods.len == 0 +} + +fn test_multi_return_sym() { + func := reflection.get_funcs().filter(it.name == 'baz')[0] + assert func.name == 'baz' + assert func.args.len == 0 + assert func.is_variadic == false + assert func.return_typ.has_flag(.option) == false + assert func.return_typ.has_flag(.result) == false + assert func.return_typ.has_flag(.shared_f) == false + assert func.receiver_typ == 0 + assert func.is_pub == false + + typ := reflection.get_type(func.return_typ)? + assert typ.name == '(int, f64, string)' + assert typ.sym.language == .v + assert typ.sym.kind == .multi_return +} + +fn test_enum_sym() { + var := reflection.type_of(Flags.foo) + assert var.sym.name == 'main.Flags' + assert var.sym.parent_idx == 0 + assert var.sym.kind == .enum_ + assert var.sym.language == .v + assert (var.sym.info as reflection.Enum).vals == ['foo', 'bar'] +} + +fn test_struct_sym() { + var := reflection.type_of(Test{}) + assert var.sym.kind == .struct_ + assert (var.sym.info as reflection.Struct).attrs.len == 1 + assert (var.sym.info as reflection.Struct).attrs == ['test_struct'] + + field := (var.sym.info as reflection.Struct).fields[0] + field_typ := field.typ + field_sym := reflection.get_type(field_typ.idx())? + assert field_sym.name == 'map[int]string' + assert field_sym.sym.kind == .map + assert (field_sym.sym.info as reflection.Map).key_type.idx() == typeof[int]().idx + assert (field_sym.sym.info as reflection.Map).value_type.idx() == typeof[string]().idx + assert field.attrs.len == 1 + + field2 := (var.sym.info as reflection.Struct).fields[1] + field2_typ := (var.sym.info as reflection.Struct).fields[1].typ + assert field2_typ.has_flag(.option) + assert field2.name == 'n' + assert field2.attrs.len == 2 + assert field2.attrs == ['test2', 'test3'] + assert field2.is_pub == false +} diff --git a/vlib/v/tests/reflection_test.v b/vlib/v/tests/reflection_test.v index 01928bb326..a465e41780 100644 --- a/vlib/v/tests/reflection_test.v +++ b/vlib/v/tests/reflection_test.v @@ -45,7 +45,7 @@ fn test_type_name() { fn test_type_symbol() { ret_typ := reflection.get_funcs().filter(it.name == 'test3')[0].return_typ - assert reflection.get_type_symbol(ret_typ)?.language == 'v' + assert reflection.get_type_symbol(ret_typ)?.language == .v } fn test_method() { @@ -76,7 +76,7 @@ fn test_interfaces() { } fn test_enum_fields() { - assert reflection.get_enums().filter(it.name == 'TestEnum')[0].fields.map(it.name) == [ + assert (reflection.get_enums().filter(it.name == 'TestEnum')[0].sym.info as reflection.Enum).vals == [ 'foo', 'bar', ]