mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
vlib: add a v.reflection
module for reflection done at runtime (#17072)
This commit is contained in:
parent
e32ed368ca
commit
84b99ceeb2
@ -236,6 +236,12 @@ mut:
|
||||
// out_idx int
|
||||
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
|
||||
reflection_strings &map[string]int
|
||||
}
|
||||
|
||||
// global or const variable definition string
|
||||
@ -264,6 +270,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string,
|
||||
$if time_cgening ? {
|
||||
timers_should_print = true
|
||||
}
|
||||
mut reflection_strings := map[string]int{}
|
||||
mut global_g := Gen{
|
||||
file: 0
|
||||
out: strings.new_builder(512000)
|
||||
@ -306,7 +313,12 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string,
|
||||
use_segfault_handler: !('no_segfault_handler' in pref.compile_defines
|
||||
|| 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
|
||||
}
|
||||
|
||||
/*
|
||||
global_g.out_parallel = []strings.Builder{len: nr_cpus}
|
||||
for i in 0 .. nr_cpus {
|
||||
@ -358,6 +370,7 @@ 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
|
||||
|
||||
@ -455,6 +468,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string,
|
||||
|
||||
mut g := global_g
|
||||
util.timing_start('cgen common')
|
||||
|
||||
// to make sure type idx's are the same in cached mods
|
||||
if g.pref.build_mode == .build_module {
|
||||
for idx, sym in g.table.type_symbols {
|
||||
@ -664,6 +678,10 @@ fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) &Gen {
|
||||
referenced_fns: global_g.referenced_fns
|
||||
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()
|
||||
return g
|
||||
@ -715,7 +733,6 @@ pub fn (mut g Gen) gen_file() {
|
||||
g.is_vlines_enabled = true
|
||||
g.inside_ternary = 0
|
||||
}
|
||||
|
||||
g.stmts(g.file.stmts)
|
||||
// Transfer embedded files
|
||||
for path in g.file.embedded_files {
|
||||
@ -5277,6 +5294,7 @@ fn (mut g Gen) write_init_function() {
|
||||
|
||||
// ___argv is declared as voidptr here, because that unifies the windows/unix logic
|
||||
g.writeln('void _vinit(int ___argc, voidptr ___argv) {')
|
||||
|
||||
if g.pref.trace_calls {
|
||||
g.writeln('\tv__trace_calls__on_call(_SLIT("_vinit"));')
|
||||
}
|
||||
@ -5302,7 +5320,20 @@ fn (mut g Gen) write_init_function() {
|
||||
if g.nr_closures > 0 {
|
||||
g.writeln('\t_closure_mtx_init();')
|
||||
}
|
||||
|
||||
// reflection bootstraping
|
||||
if g.has_reflection {
|
||||
if var := g.global_const_defs['g_reflection'] {
|
||||
g.writeln(var.init)
|
||||
g.gen_reflection_data()
|
||||
}
|
||||
}
|
||||
|
||||
for mod_name in g.table.modules {
|
||||
if g.has_reflection && mod_name == 'v.reflection' {
|
||||
// ignore v.reflection already initialized above
|
||||
continue
|
||||
}
|
||||
mut is_empty := true
|
||||
// write globals and consts init later
|
||||
for var_name in g.sorted_global_const_names {
|
||||
@ -5328,6 +5359,7 @@ fn (mut g Gen) write_init_function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.writeln('}')
|
||||
if g.pref.printfn_list.len > 0 && '_vinit' in g.pref.printfn_list {
|
||||
println(g.out.after(fn_vinit_start_pos))
|
||||
|
@ -288,6 +288,7 @@ 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
|
||||
|
160
vlib/v/gen/c/reflection.v
Normal file
160
vlib/v/gen/c/reflection.v
Normal file
@ -0,0 +1,160 @@
|
||||
module c
|
||||
|
||||
import v.ast
|
||||
import v.util
|
||||
|
||||
// reflection_string maps string to its idx
|
||||
fn (mut g Gen) reflection_string(str string) int {
|
||||
return unsafe {
|
||||
g.reflection_strings[str] or {
|
||||
g.reflection_strings[str] = g.reflection_strings.len
|
||||
g.reflection_strings.len - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gen_reflection_strings generates the reflectino string registration
|
||||
[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')
|
||||
}
|
||||
}
|
||||
|
||||
// gen_empty_array generates code for empty array
|
||||
[inline]
|
||||
fn (g Gen) gen_empty_array(type_name string) string {
|
||||
return '__new_array_with_default(0, 0, sizeof(${type_name}), 0)'
|
||||
}
|
||||
|
||||
// gen_functionarg_array generates the code for functionarg argument
|
||||
[inline]
|
||||
fn (g Gen) gen_functionarg_array(type_name string, node ast.FnDecl) 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 += '}))'
|
||||
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'
|
||||
|
||||
if nodes.len == 0 {
|
||||
return g.gen_empty_array(type_name)
|
||||
}
|
||||
|
||||
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 += '}))'
|
||||
return out
|
||||
}
|
||||
|
||||
// gen_reflection_enum_fields generates C code for enum fields
|
||||
[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){'
|
||||
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 += '.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 arg_str
|
||||
}
|
||||
|
||||
// gen_reflection_sym generates C code for TypeSymbol struct
|
||||
[inline]
|
||||
fn (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}}'
|
||||
}
|
||||
|
||||
// gen_reflection_function generates C code for reflection function metadata
|
||||
[inline]
|
||||
fn (mut g Gen) gen_reflection_function(node ast.FnDecl) {
|
||||
if !g.has_reflection {
|
||||
return
|
||||
}
|
||||
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')
|
||||
}
|
||||
|
||||
// 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.gen_reflection_strings()
|
||||
|
||||
// funcs meta info filling
|
||||
g.writeln(g.reflection_funcs.str())
|
||||
|
||||
// others meta info filling
|
||||
g.writeln(g.reflection_others.str())
|
||||
}
|
194
vlib/v/reflection/reflection.v
Normal file
194
vlib/v/reflection/reflection.v
Normal file
@ -0,0 +1,194 @@
|
||||
[has_globals]
|
||||
module reflection
|
||||
|
||||
import v.ast
|
||||
|
||||
__global g_reflection = Reflection{}
|
||||
|
||||
[heap; minify]
|
||||
pub struct Reflection {
|
||||
pub mut:
|
||||
modules []Module
|
||||
funcs []Function
|
||||
types []Type
|
||||
type_symbols []TypeSymbol
|
||||
enums []Enum
|
||||
interfaces []Interface
|
||||
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 struct EnumField {
|
||||
pub:
|
||||
name string // field name
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub struct Type {
|
||||
pub:
|
||||
name string // type name
|
||||
idx int // type idx
|
||||
sym TypeSymbol // type symbol
|
||||
}
|
||||
|
||||
pub struct Module {
|
||||
pub:
|
||||
name string // module name
|
||||
}
|
||||
|
||||
pub struct FunctionArg {
|
||||
pub:
|
||||
name string // argument name
|
||||
typ int // argument type idx
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
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)
|
||||
}
|
||||
|
||||
// API module
|
||||
|
||||
pub fn get_string_by_idx(idx int) string {
|
||||
return g_reflection.strings[idx]
|
||||
}
|
||||
|
||||
// type_of returns the type info of the passed value
|
||||
pub fn type_of[T](val T) Type {
|
||||
return g_reflection.types.filter(it.idx == typeof[T]().idx)[0]
|
||||
}
|
||||
|
||||
// get_modules returns the module name built with V source
|
||||
pub fn get_modules() []Module {
|
||||
return g_reflection.modules
|
||||
}
|
||||
|
||||
// get_functions returns the functions built with V source
|
||||
pub fn get_funcs() []Function {
|
||||
return g_reflection.funcs
|
||||
}
|
||||
|
||||
// get_types returns the registered types
|
||||
pub fn get_types() []Type {
|
||||
return g_reflection.types
|
||||
}
|
||||
|
||||
// get_enums returns the registered enums
|
||||
pub fn get_enums() []Enum {
|
||||
return g_reflection.enums
|
||||
}
|
||||
|
||||
// get_aliases returns the registered aliases
|
||||
pub fn get_aliases() []Type {
|
||||
alias_idxs := g_reflection.type_symbols.filter(it.kind == .alias).map(it.idx)
|
||||
return g_reflection.types.filter(it.idx in alias_idxs)
|
||||
}
|
||||
|
||||
// get_interfaces returns the registered aliases
|
||||
pub fn get_interfaces() []Interface {
|
||||
return g_reflection.interfaces
|
||||
}
|
||||
|
||||
// get_sum_types returns the registered sum types
|
||||
pub fn get_sum_types() []Type {
|
||||
sumtype_idxs := g_reflection.type_symbols.filter(it.kind == .sum_type).map(it.idx)
|
||||
return g_reflection.types.filter(it.idx in sumtype_idxs)
|
||||
}
|
||||
|
||||
// get_type_symbol returns the registered type symbols
|
||||
pub fn get_type_symbols() []TypeSymbol {
|
||||
return g_reflection.type_symbols
|
||||
}
|
||||
|
||||
// Type API
|
||||
pub fn type_name(idx int) string {
|
||||
t := g_reflection.types.filter(it.idx == idx)
|
||||
return if t.len != 0 { t[0].name } else { '' }
|
||||
}
|
||||
|
||||
pub fn get_type(idx int) ?Type {
|
||||
t := g_reflection.types.filter(it.idx == idx)
|
||||
return if t.len != 0 { t[0] } else { none }
|
||||
}
|
||||
|
||||
// Type Symbol API
|
||||
pub fn type_symbol_name(idx int) string {
|
||||
t := g_reflection.type_symbols.filter(it.idx == idx)
|
||||
return if t.len != 0 { t[0].name } else { '' }
|
||||
}
|
||||
|
||||
pub fn get_type_symbol(idx int) ?TypeSymbol {
|
||||
t := g_reflection.type_symbols.filter(it.idx == idx)
|
||||
return if t.len != 0 { t[0] } else { none }
|
||||
}
|
||||
|
||||
// V reflection metainfo API (called from backend to fill metadata info)
|
||||
|
||||
[markused]
|
||||
fn add_module(mod_name string) {
|
||||
g_reflection.modules << Module{mod_name}
|
||||
}
|
||||
|
||||
[markused]
|
||||
fn add_func(func Function) {
|
||||
g_reflection.funcs << func
|
||||
}
|
||||
|
||||
[markused]
|
||||
fn add_type(type_ Type) {
|
||||
g_reflection.types << type_
|
||||
}
|
||||
|
||||
[markused]
|
||||
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
|
||||
}
|
88
vlib/v/tests/reflection_test.v
Normal file
88
vlib/v/tests/reflection_test.v
Normal file
@ -0,0 +1,88 @@
|
||||
import v.reflection
|
||||
|
||||
type MyInt = int
|
||||
|
||||
type MySumType = f64 | int
|
||||
|
||||
enum TestEnum {
|
||||
foo
|
||||
bar
|
||||
}
|
||||
|
||||
struct User {
|
||||
name string
|
||||
}
|
||||
|
||||
fn (u User) get_name() string {
|
||||
return u.name
|
||||
}
|
||||
|
||||
fn test2(arg []string) {}
|
||||
|
||||
[noreturn]
|
||||
fn test3(a reflection.Function) {
|
||||
}
|
||||
|
||||
fn test_module_existing() {
|
||||
assert 'v.reflection' in reflection.get_modules().map(it.name)
|
||||
}
|
||||
|
||||
fn test_func_attribute() {
|
||||
assert reflection.get_funcs().filter(it.name == 'test3')[0].is_variadic == false
|
||||
}
|
||||
|
||||
fn test_func_name() {
|
||||
assert reflection.get_funcs().filter(it.name == 'test2')[0].name == 'test2'
|
||||
}
|
||||
|
||||
fn test_type_name() {
|
||||
ret_typ := reflection.get_funcs().filter(it.name == 'test3')[0].return_typ
|
||||
assert reflection.type_name(ret_typ) == 'void'
|
||||
assert reflection.get_type(ret_typ)?.name == 'void'
|
||||
assert reflection.get_type_symbol(ret_typ)?.name == 'void'
|
||||
assert reflection.type_name(reflection.get_funcs().filter(it.name == 'test3')[0].args[0].typ) == 'Function'
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
fn test_method() {
|
||||
method := reflection.get_funcs().filter(it.name == 'get_name')[0]
|
||||
assert reflection.type_name(method.return_typ) == 'string'
|
||||
println(reflection.get_type(method.receiver_typ)?.name)
|
||||
assert reflection.get_type(method.receiver_typ)?.name == 'User'
|
||||
}
|
||||
|
||||
fn test_enum() {
|
||||
assert reflection.get_enums().filter(it.name == 'TestEnum')[0].name == 'TestEnum'
|
||||
}
|
||||
|
||||
fn test_get_aliases() {
|
||||
assert reflection.get_aliases().filter(it.name == 'MyInt')[0].name == 'MyInt'
|
||||
}
|
||||
|
||||
fn test_get_sum_types() {
|
||||
assert reflection.get_sum_types().filter(it.name == 'MySumType')[0].name == 'MySumType'
|
||||
}
|
||||
|
||||
fn test_get_interfaces() {
|
||||
assert reflection.get_interfaces().filter(it.name == 'IError')[0].name == 'IError'
|
||||
}
|
||||
|
||||
fn test_interfaces() {
|
||||
assert reflection.get_interfaces().filter(it.name == 'IError')[0].methods.len == 2
|
||||
}
|
||||
|
||||
fn test_enum_fields() {
|
||||
assert reflection.get_enums().filter(it.name == 'TestEnum')[0].fields.map(it.name) == [
|
||||
'foo',
|
||||
'bar',
|
||||
]
|
||||
}
|
||||
|
||||
fn test_get_string_by_idx() {
|
||||
file_idx := reflection.get_funcs().filter(it.name == 'all_after_last')[0].file_idx
|
||||
assert reflection.get_string_by_idx(file_idx).ends_with('string.v')
|
||||
}
|
Loading…
Reference in New Issue
Block a user