mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
1640 lines
42 KiB
V
1640 lines
42 KiB
V
// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
|
|
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
|
module markused
|
|
|
|
// Walk the entire program starting at fn main and marks used (called) functions.
|
|
// Unused functions can be safely skipped by the backends to save CPU time and space.
|
|
import v.ast
|
|
import v.pref
|
|
|
|
pub struct Walker {
|
|
pub mut:
|
|
table &ast.Table = unsafe { nil }
|
|
features &ast.UsedFeatures = unsafe { nil }
|
|
used_fns map[string]bool // used_fns['println'] == true
|
|
trace_enabled bool
|
|
used_consts map[string]bool // used_consts['os.args'] == true
|
|
used_globals map[string]bool
|
|
used_fields map[string]bool
|
|
used_structs map[string]bool
|
|
used_types map[ast.Type]bool
|
|
used_syms map[int]bool
|
|
used_arr_method map[string]bool
|
|
used_map_method map[string]bool
|
|
used_none int // _option_none
|
|
used_option int // _option_ok
|
|
used_result int // _result_ok
|
|
used_panic int // option/result propagation
|
|
used_closures int // fn [x] (){}, and `instance.method` used in an expression
|
|
pref &pref.Preferences = unsafe { nil }
|
|
mut:
|
|
all_fns map[string]ast.FnDecl
|
|
all_consts map[string]ast.ConstField
|
|
all_globals map[string]ast.GlobalField
|
|
all_fields map[string]ast.StructField
|
|
all_decltypes map[string]ast.TypeDecl
|
|
all_structs map[string]ast.StructDecl
|
|
|
|
level int
|
|
is_builtin_mod bool
|
|
is_direct_array_access bool
|
|
|
|
// dependencies finding flags
|
|
uses_atomic bool // has atomic
|
|
uses_array bool // has array
|
|
uses_array_sumtype bool // has array on sumtype init
|
|
uses_channel bool // has chan dep
|
|
uses_lock bool // has mutex dep
|
|
uses_ct_fields bool // $for .fields
|
|
uses_ct_methods bool // $for .methods
|
|
uses_ct_params bool // $for .params
|
|
uses_ct_values bool // $for .values
|
|
uses_ct_variants bool // $for .variants
|
|
uses_ct_attribute bool // $for .attributes
|
|
uses_external_type bool
|
|
uses_err bool // err var
|
|
uses_asserts bool // assert
|
|
uses_map_update bool // has {...expr}
|
|
uses_debugger bool // has debugger;
|
|
uses_mem_align bool // @[aligned:N] for structs
|
|
uses_eq bool // has == op
|
|
uses_interp bool // string interpolation
|
|
uses_guard bool
|
|
uses_orm bool
|
|
uses_str map[ast.Type]bool // has .str() calls, and for which types
|
|
uses_free map[ast.Type]bool // has .free() calls, and for which types
|
|
uses_spawn bool
|
|
uses_dump bool
|
|
uses_memdup bool // sumtype cast and &Struct{}
|
|
uses_arr_void bool // auto arr methods
|
|
uses_index bool // var[k]
|
|
uses_index_check bool // var[k] or { }
|
|
uses_arr_range_index bool // arr[i..j]
|
|
uses_str_range_index bool // str[i..j]
|
|
uses_range_index_check bool // var[i..j] or { }
|
|
uses_arr_range_index_gated bool
|
|
uses_str_range_index_gated bool
|
|
uses_str_index bool // string[k]
|
|
uses_str_index_check bool // string[k] or { }
|
|
uses_str_range bool // string[a..b]
|
|
uses_fixed_arr_int bool // fixed_arr[k]
|
|
uses_append bool // var << item
|
|
uses_map_setter bool
|
|
uses_map_getter bool
|
|
uses_arr_setter bool
|
|
uses_arr_getter bool
|
|
uses_arr_clone bool
|
|
uses_arr_sorted bool
|
|
}
|
|
|
|
pub fn Walker.new(params Walker) &Walker {
|
|
mut new_walker := &Walker{
|
|
...params
|
|
}
|
|
new_walker.features = params.table.used_features
|
|
return new_walker
|
|
}
|
|
|
|
@[inline]
|
|
fn (mut w Walker) mark_fn_as_used(fkey string) {
|
|
$if trace_skip_unused_marked ? {
|
|
eprintln(' fn > |${fkey}|')
|
|
}
|
|
w.used_fns[fkey] = true
|
|
}
|
|
|
|
@[inline]
|
|
pub fn (mut w Walker) mark_builtin_array_method_as_used(method_name string) {
|
|
w.mark_builtin_type_method_as_used('${ast.array_type_idx}.${method_name}', '${int(ast.array_type.ref())}.${method_name}')
|
|
}
|
|
|
|
@[inline]
|
|
pub fn (mut w Walker) mark_builtin_map_method_as_used(method_name string) {
|
|
w.mark_builtin_type_method_as_used('${ast.map_type_idx}.${method_name}', '${int(ast.map_type.ref())}.${method_name}')
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_builtin_type_method_as_used(k string, rk string) {
|
|
if mut cfn := w.all_fns[k] {
|
|
w.fn_decl(mut cfn)
|
|
} else if mut cfn := w.all_fns[rk] {
|
|
w.fn_decl(mut cfn)
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_const_as_used(ckey string) {
|
|
$if trace_skip_unused_marked ? {
|
|
eprintln(' const > |${ckey}|')
|
|
}
|
|
if w.used_consts[ckey] {
|
|
return
|
|
}
|
|
w.used_consts[ckey] = true
|
|
cfield := w.all_consts[ckey] or { return }
|
|
w.expr(cfield.expr)
|
|
w.mark_by_type(cfield.typ)
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_global_as_used(ckey string) {
|
|
$if trace_skip_unused_marked ? {
|
|
eprintln(' global > |${ckey}|')
|
|
}
|
|
if w.used_globals[ckey] {
|
|
return
|
|
}
|
|
w.used_globals[ckey] = true
|
|
gfield := w.all_globals[ckey] or { return }
|
|
w.table.used_features.used_attr_weak = w.table.used_features.used_attr_weak || gfield.is_weak
|
|
w.table.used_features.used_attr_hidden = w.table.used_features.used_attr_hidden
|
|
|| gfield.is_hidden || gfield.is_hidden
|
|
w.expr(gfield.expr)
|
|
if !gfield.has_expr {
|
|
w.mark_by_type(gfield.typ)
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_struct_field_default_expr_as_used(sfkey string) {
|
|
if w.used_fields[sfkey] {
|
|
return
|
|
}
|
|
w.used_fields[sfkey] = true
|
|
sfield := w.all_fields[sfkey] or { return }
|
|
w.expr(sfield.default_expr)
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_markused_fns() {
|
|
for _, mut func in w.all_fns {
|
|
// @[export]
|
|
// @[markused]
|
|
if func.is_exported || func.is_markused {
|
|
$if trace_skip_unused_exported_fns ? {
|
|
if func.is_exported {
|
|
println('>>>> walking exported func: ${func.name} ...')
|
|
}
|
|
if func.is_markused {
|
|
println('>>>> walking markused func: ${func.name} ...')
|
|
}
|
|
}
|
|
w.fn_decl(mut func)
|
|
continue
|
|
}
|
|
// veb actions
|
|
if func.return_type == w.table.veb_res_idx_cache {
|
|
$if trace_skip_veb_actions ? {
|
|
println('>>>> walking veb action func: ${func.name} ...')
|
|
}
|
|
w.fn_decl(mut func)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) {
|
|
if w.trace_enabled {
|
|
eprintln('>>>>>>> ROOT ${all_fn_root_names}')
|
|
}
|
|
for fn_name in all_fn_root_names {
|
|
if fn_name !in w.used_fns {
|
|
$if trace_skip_unused_roots ? {
|
|
println('>>>> walking root func: ${fn_name} ...')
|
|
}
|
|
unsafe { w.fn_decl(mut w.all_fns[fn_name]) }
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_markused_consts() {
|
|
for ckey, mut constfield in w.all_consts {
|
|
if constfield.is_markused {
|
|
$if trace_skip_unused_markused_consts ? {
|
|
println('>>>> walking markused const: ${ckey}')
|
|
}
|
|
w.mark_const_as_used(ckey)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_markused_globals() {
|
|
for gkey, mut globalfield in w.all_globals {
|
|
if globalfield.is_markused || globalfield.is_exported {
|
|
$if trace_skip_unused_markused_globals ? {
|
|
println('>>>> walking markused global: ${gkey}')
|
|
}
|
|
w.mark_global_as_used(gkey)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_markused_syms() {
|
|
for sym in w.table.type_symbols {
|
|
if sym.info is ast.Struct && sym.info.is_markused {
|
|
w.mark_by_sym(sym)
|
|
} else if sym.info is ast.Interface && sym.info.is_markused {
|
|
w.mark_by_sym(sym)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_markused_decltypes() {
|
|
for _, decl in w.all_decltypes {
|
|
if decl.is_markused {
|
|
w.mark_by_type(decl.typ)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) stmt(node_ ast.Stmt) {
|
|
mut node := unsafe { node_ }
|
|
match mut node {
|
|
ast.EmptyStmt {}
|
|
ast.DebuggerStmt {
|
|
w.uses_debugger = true
|
|
}
|
|
ast.AsmStmt {
|
|
w.asm_io(node.output)
|
|
w.asm_io(node.input)
|
|
}
|
|
ast.AssertStmt {
|
|
if node.is_used {
|
|
w.uses_asserts = true
|
|
w.expr(node.expr)
|
|
if node.extra !is ast.EmptyExpr {
|
|
w.expr(node.extra)
|
|
}
|
|
}
|
|
}
|
|
ast.AssignStmt {
|
|
w.exprs(node.left)
|
|
w.exprs(node.right)
|
|
}
|
|
ast.Block {
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.ComptimeFor {
|
|
w.mark_by_type(node.typ)
|
|
w.stmts(node.stmts)
|
|
match node.kind {
|
|
.attributes {
|
|
w.uses_ct_attribute = true
|
|
}
|
|
.variants {
|
|
w.uses_ct_variants = true
|
|
}
|
|
.params {
|
|
w.uses_ct_params = true
|
|
}
|
|
.values {
|
|
w.uses_ct_values = true
|
|
}
|
|
.fields {
|
|
w.uses_ct_fields = true
|
|
}
|
|
.methods {
|
|
w.uses_ct_methods = true
|
|
}
|
|
}
|
|
}
|
|
ast.ConstDecl {
|
|
w.const_fields(node.fields)
|
|
}
|
|
ast.ExprStmt {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.FnDecl {
|
|
w.fn_decl(mut node)
|
|
}
|
|
ast.ForCStmt {
|
|
if node.has_init {
|
|
w.stmt(node.init)
|
|
}
|
|
if node.has_cond {
|
|
w.expr(node.cond)
|
|
}
|
|
if node.has_inc {
|
|
w.stmt(node.inc)
|
|
}
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.ForInStmt {
|
|
w.expr(node.cond)
|
|
w.expr(node.high)
|
|
w.stmts(node.stmts)
|
|
w.mark_by_type(node.cond_type)
|
|
if node.kind == .map {
|
|
w.features.used_maps++
|
|
} else if node.kind == .struct {
|
|
if node.cond_type == 0 {
|
|
return
|
|
}
|
|
// the .next() method of the struct will be used for iteration:
|
|
cond_type_sym := w.table.sym(node.cond_type)
|
|
if next_fn := cond_type_sym.find_method('next') {
|
|
unsafe {
|
|
w.fn_decl(mut &ast.FnDecl(next_fn.source_fn))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ast.ForStmt {
|
|
if !node.is_inf {
|
|
w.expr(node.cond)
|
|
}
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.Return {
|
|
w.exprs(node.exprs)
|
|
}
|
|
ast.SqlStmt {
|
|
w.expr(node.db_expr)
|
|
w.expr(node.or_expr)
|
|
for line in node.lines {
|
|
if line.table_expr.typ != 0 {
|
|
w.mark_by_sym(w.table.sym(line.table_expr.typ))
|
|
}
|
|
w.expr(line.where_expr)
|
|
w.exprs(line.update_exprs)
|
|
}
|
|
w.uses_orm = true
|
|
}
|
|
ast.StructDecl {
|
|
for typ in node.implements_types {
|
|
w.mark_by_type(typ.typ)
|
|
}
|
|
w.struct_fields(node.fields)
|
|
w.uses_mem_align = w.uses_mem_align || node.is_aligned
|
|
}
|
|
ast.DeferStmt {
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.GlobalDecl {
|
|
for gf in node.fields {
|
|
if gf.has_expr {
|
|
w.expr(gf.expr)
|
|
}
|
|
}
|
|
}
|
|
ast.BranchStmt {}
|
|
ast.EnumDecl {}
|
|
ast.GotoLabel {}
|
|
ast.GotoStmt {}
|
|
ast.HashStmt {}
|
|
ast.Import {}
|
|
ast.InterfaceDecl {}
|
|
ast.SemicolonStmt {}
|
|
ast.Module {}
|
|
ast.TypeDecl {}
|
|
ast.NodeError {}
|
|
}
|
|
}
|
|
|
|
fn (mut w Walker) asm_io(ios []ast.AsmIO) {
|
|
for io in ios {
|
|
w.expr(io.expr)
|
|
}
|
|
}
|
|
|
|
fn (mut w Walker) defer_stmts(stmts []ast.DeferStmt) {
|
|
for stmt in stmts {
|
|
w.stmts(stmt.stmts)
|
|
}
|
|
}
|
|
|
|
fn (mut w Walker) stmts(stmts []ast.Stmt) {
|
|
for stmt in stmts {
|
|
w.stmt(stmt)
|
|
}
|
|
}
|
|
|
|
fn (mut w Walker) exprs(exprs []ast.Expr) {
|
|
for expr in exprs {
|
|
w.expr(expr)
|
|
}
|
|
}
|
|
|
|
fn (mut w Walker) expr(node_ ast.Expr) {
|
|
mut node := unsafe { node_ }
|
|
match mut node {
|
|
ast.EmptyExpr {
|
|
// TODO: make sure this doesn't happen
|
|
// panic('Walker: EmptyExpr')
|
|
}
|
|
ast.ComptimeType {}
|
|
ast.AnonFn {
|
|
w.fn_decl(mut node.decl)
|
|
}
|
|
ast.ArrayInit {
|
|
sym := w.table.sym(node.elem_type)
|
|
w.mark_by_sym(sym)
|
|
if sym.info is ast.Thread {
|
|
w.mark_by_type(w.table.find_or_register_array(sym.info.return_type))
|
|
}
|
|
w.mark_by_type(node.typ)
|
|
w.expr(node.len_expr)
|
|
w.expr(node.cap_expr)
|
|
w.expr(node.init_expr)
|
|
w.exprs(node.exprs)
|
|
if !w.uses_array && !w.is_direct_array_access {
|
|
w.uses_array = true
|
|
}
|
|
if node.elem_type.has_flag(.option) {
|
|
w.used_option++
|
|
}
|
|
}
|
|
ast.Assoc {
|
|
w.exprs(node.exprs)
|
|
}
|
|
ast.ArrayDecompose {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.CallExpr {
|
|
w.call_expr(mut node)
|
|
if node.name == 'json.decode' {
|
|
w.mark_by_type((node.args[0].expr as ast.TypeNode).typ)
|
|
} else if node.name == 'json.encode' && node.args[0].typ != 0 {
|
|
sym := w.table.final_sym(node.args[0].typ)
|
|
if sym.info is ast.Map {
|
|
w.mark_by_type(w.table.find_or_register_array(sym.info.key_type))
|
|
}
|
|
} else if !node.is_method && node.args.len == 1 && node.args[0].typ != ast.string_type
|
|
&& node.name in ['println', 'print', 'eprint', 'eprintln'] {
|
|
w.uses_str[node.args[0].typ] = true
|
|
} else if node.is_method && node.name == 'str' {
|
|
w.uses_str[node.left_type] = true
|
|
} else if node.is_method && node.name == 'free' {
|
|
w.uses_free[node.left_type] = true
|
|
} else if node.is_method && node.name == 'clone' && !w.uses_arr_clone
|
|
&& node.left_type != 0 && w.table.final_sym(node.left_type).kind == .array {
|
|
w.uses_arr_clone = true
|
|
} else if node.is_method && node.name == 'sorted' && !w.uses_arr_sorted
|
|
&& node.left_type != 0 && w.table.final_sym(node.left_type).kind == .array {
|
|
w.uses_arr_sorted = true
|
|
}
|
|
if !w.is_builtin_mod && !w.uses_external_type {
|
|
if node.is_method {
|
|
w.uses_external_type = node.mod == 'builtin'
|
|
} else if node.name.contains('.') && node.name.all_before_last('.').len > 1 {
|
|
w.uses_external_type = true
|
|
}
|
|
}
|
|
if node.is_method && node.left_type != 0
|
|
&& w.table.final_sym(node.left_type).kind in [.array_fixed, .array] {
|
|
w.mark_by_type(node.return_type)
|
|
}
|
|
}
|
|
ast.CastExpr {
|
|
w.expr(node.expr)
|
|
w.expr(node.arg)
|
|
if !w.uses_memdup {
|
|
fsym := w.table.final_sym(node.typ)
|
|
w.uses_memdup = fsym.kind == .sum_type
|
|
}
|
|
w.mark_by_type(node.typ)
|
|
if node.typ.has_flag(.option) {
|
|
w.used_option++
|
|
} else if node.typ.has_flag(.result) {
|
|
w.used_result++
|
|
}
|
|
}
|
|
ast.ChanInit {
|
|
w.expr(node.cap_expr)
|
|
w.mark_by_type(node.typ)
|
|
w.uses_channel = true
|
|
}
|
|
ast.ConcatExpr {
|
|
w.exprs(node.vals)
|
|
w.mark_by_sym(w.table.sym(node.return_type))
|
|
}
|
|
ast.ComptimeSelector {
|
|
w.expr(node.left)
|
|
w.expr(node.field_expr)
|
|
}
|
|
ast.ComptimeCall {
|
|
w.expr(node.left)
|
|
if node.is_vweb {
|
|
w.stmts(node.veb_tmpl.stmts)
|
|
}
|
|
if node.kind == .embed_file {
|
|
w.features.used_maps++
|
|
}
|
|
}
|
|
ast.DumpExpr {
|
|
w.expr(node.expr)
|
|
w.features.dump = true
|
|
w.mark_by_type(node.expr_type)
|
|
}
|
|
ast.SpawnExpr {
|
|
if node.is_expr {
|
|
w.fn_by_name('free')
|
|
}
|
|
w.mark_by_type(w.table.find_or_register_thread(node.call_expr.return_type))
|
|
w.expr(node.call_expr)
|
|
w.uses_spawn = true
|
|
|
|
if w.pref.os == .windows {
|
|
w.fn_by_name('panic_lasterr')
|
|
w.fn_by_name('winapi_lasterr_str')
|
|
} else {
|
|
w.fn_by_name('c_error_number_str')
|
|
w.fn_by_name('panic_error_number')
|
|
}
|
|
}
|
|
ast.GoExpr {
|
|
if node.is_expr {
|
|
w.fn_by_name('free')
|
|
}
|
|
w.mark_by_type(node.call_expr.return_type)
|
|
w.expr(node.call_expr)
|
|
}
|
|
ast.IndexExpr {
|
|
w.expr(node.left)
|
|
w.expr(node.index)
|
|
if node.or_expr.kind == .block {
|
|
w.uses_index_check = true
|
|
}
|
|
w.mark_by_type(node.typ)
|
|
w.or_block(node.or_expr)
|
|
if node.left_type == 0 {
|
|
return
|
|
}
|
|
sym := w.table.final_sym(node.left_type)
|
|
if sym.info is ast.Map {
|
|
if node.is_setter && !w.uses_map_setter {
|
|
w.mark_builtin_map_method_as_used('set')
|
|
} else if !node.is_setter && !w.uses_map_getter {
|
|
w.mark_builtin_map_method_as_used('get')
|
|
}
|
|
w.mark_by_type(sym.info.key_type)
|
|
w.mark_by_type(sym.info.value_type)
|
|
w.features.used_maps++
|
|
} else if sym.kind in [.array, .array_fixed, .any] {
|
|
if !w.is_direct_array_access || w.features.auto_str_arr {
|
|
if node.is_setter && !w.uses_arr_setter {
|
|
w.mark_builtin_array_method_as_used('set')
|
|
w.uses_arr_setter = true
|
|
} else if !node.is_setter && !w.uses_arr_getter {
|
|
w.mark_builtin_array_method_as_used('get')
|
|
w.uses_arr_getter = true
|
|
}
|
|
}
|
|
if sym.info is ast.Array {
|
|
w.mark_by_type(sym.info.elem_type)
|
|
} else if sym.info is ast.ArrayFixed {
|
|
w.mark_by_type(sym.info.elem_type)
|
|
}
|
|
if !w.uses_arr_range_index {
|
|
w.uses_arr_range_index = true
|
|
}
|
|
if !w.uses_fixed_arr_int && sym.kind == .array_fixed {
|
|
w.uses_fixed_arr_int = true
|
|
}
|
|
if !w.uses_index && !w.is_direct_array_access {
|
|
w.uses_index = true
|
|
}
|
|
if !w.uses_arr_range_index_gated {
|
|
w.uses_arr_range_index_gated = node.is_gated
|
|
}
|
|
} else if sym.kind == .string {
|
|
w.uses_str_index = true
|
|
if node.index is ast.RangeExpr {
|
|
if !w.uses_str_range_index {
|
|
w.uses_str_range_index = true
|
|
}
|
|
if !w.uses_range_index_check {
|
|
w.uses_range_index_check = node.or_expr.kind == .block
|
|
}
|
|
if !w.uses_str_range_index_gated {
|
|
w.uses_str_range_index_gated = node.is_gated
|
|
}
|
|
} else {
|
|
if !w.uses_str_index_check {
|
|
w.uses_str_index_check = node.or_expr.kind == .block
|
|
}
|
|
if !w.uses_str_range {
|
|
w.uses_str_range = node.index is ast.RangeExpr
|
|
}
|
|
}
|
|
} else if sym.info is ast.Struct {
|
|
w.mark_by_sym(sym)
|
|
} else if sym.info is ast.SumType {
|
|
w.mark_by_sym(sym)
|
|
} else if sym.kind == .any {
|
|
if !w.is_direct_array_access {
|
|
if node.is_setter && !w.uses_arr_setter {
|
|
w.mark_builtin_array_method_as_used('set')
|
|
} else if !node.is_setter && !w.uses_arr_getter {
|
|
w.mark_builtin_array_method_as_used('get')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ast.InfixExpr {
|
|
w.expr(node.left)
|
|
w.expr(node.right)
|
|
w.or_block(node.or_block)
|
|
if node.left_type != 0 {
|
|
sym := w.table.sym(node.left_type)
|
|
w.mark_by_sym(sym)
|
|
if sym.kind == .struct {
|
|
if opmethod := sym.find_method(node.op.str()) {
|
|
unsafe {
|
|
w.fn_decl(mut &ast.FnDecl(opmethod.source_fn))
|
|
}
|
|
}
|
|
} else {
|
|
if !w.uses_append && node.op == .left_shift && (sym.kind == .array
|
|
|| (sym.kind == .alias && w.table.final_sym(node.left_type).kind == .array)) {
|
|
w.uses_append = true
|
|
}
|
|
}
|
|
}
|
|
right_type := if node.right_type == 0 && mut node.right is ast.TypeNode {
|
|
node.right.typ
|
|
} else {
|
|
node.right_type
|
|
}
|
|
if right_type != 0 {
|
|
w.mark_by_type(right_type)
|
|
right_sym := w.table.sym(right_type)
|
|
if node.op in [.not_in, .key_in] {
|
|
if right_sym.kind == .map {
|
|
w.features.used_maps++
|
|
}
|
|
if !w.uses_arr_void && !w.is_direct_array_access
|
|
&& right_sym.kind in [.array, .array_fixed] {
|
|
w.uses_arr_void = true
|
|
}
|
|
} else if node.op in [.key_is, .not_is] {
|
|
w.mark_by_sym(right_sym)
|
|
}
|
|
}
|
|
if !w.uses_eq && node.op in [.eq, .ne] {
|
|
w.uses_eq = true
|
|
}
|
|
}
|
|
ast.IfGuardExpr {
|
|
w.expr(node.expr)
|
|
w.mark_by_type(node.expr_type)
|
|
w.uses_guard = true
|
|
if !w.uses_str_index_check && node.expr is ast.IndexExpr && node.expr_type != 0
|
|
&& w.table.final_sym(node.expr_type).kind in [.u8, .string] {
|
|
w.uses_str_index_check = true
|
|
}
|
|
}
|
|
ast.IfExpr {
|
|
w.expr(node.left)
|
|
for b in node.branches {
|
|
w.expr(b.cond)
|
|
w.stmts(b.stmts)
|
|
}
|
|
}
|
|
ast.Ident {
|
|
match node.kind {
|
|
.constant {
|
|
w.mark_const_as_used(node.name)
|
|
}
|
|
.function {
|
|
w.fn_by_name(node.name)
|
|
if node.info is ast.IdentFn {
|
|
w.mark_by_type(node.info.typ)
|
|
}
|
|
}
|
|
.global {
|
|
w.mark_global_as_used(node.name)
|
|
}
|
|
.blank_ident {}
|
|
else {
|
|
// `.unresolved`, `.variable`
|
|
// println('>>> else, ast.Ident ${node.name} kind: $node.kind ')
|
|
if node.name in w.all_consts {
|
|
w.mark_const_as_used(node.name)
|
|
} else if node.name in w.all_globals {
|
|
w.mark_global_as_used(node.name)
|
|
} else {
|
|
if !w.uses_err && node.name == 'err' {
|
|
w.uses_err = true
|
|
}
|
|
w.fn_by_name(node.name)
|
|
}
|
|
if !w.uses_atomic && node.info is ast.IdentVar {
|
|
w.uses_atomic = node.info.typ.has_flag(.atomic_f)
|
|
}
|
|
}
|
|
}
|
|
w.or_block(node.or_expr)
|
|
}
|
|
ast.LambdaExpr {
|
|
w.expr(node.func)
|
|
}
|
|
ast.Likely {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.MapInit {
|
|
w.exprs(node.keys)
|
|
w.exprs(node.vals)
|
|
if node.has_update_expr {
|
|
w.expr(node.update_expr)
|
|
}
|
|
if node.vals.len > 0 && node.has_update_expr {
|
|
w.uses_map_update = true
|
|
}
|
|
mapinfo := w.table.final_sym(node.typ).map_info()
|
|
ksym := w.table.sym(mapinfo.key_type)
|
|
vsym := w.table.sym(mapinfo.value_type)
|
|
if node.typ.has_flag(.shared_f) {
|
|
w.uses_lock = true
|
|
}
|
|
w.mark_by_sym(ksym)
|
|
w.mark_by_sym(vsym)
|
|
w.features.used_maps++
|
|
w.mark_by_type(node.typ)
|
|
}
|
|
ast.MatchExpr {
|
|
w.expr(node.cond)
|
|
for b in node.branches {
|
|
w.exprs(b.exprs)
|
|
for expr in b.exprs {
|
|
if expr is ast.TypeNode {
|
|
w.mark_by_sym(w.table.sym(expr.typ))
|
|
}
|
|
}
|
|
w.stmts(b.stmts)
|
|
}
|
|
}
|
|
ast.None {
|
|
w.used_none++
|
|
}
|
|
ast.Nil {}
|
|
ast.ParExpr {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.PrefixExpr {
|
|
if !w.uses_memdup && node.op == .amp {
|
|
w.uses_memdup = true
|
|
}
|
|
w.expr(node.right)
|
|
}
|
|
ast.PostfixExpr {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.RangeExpr {
|
|
if node.has_low {
|
|
w.expr(node.low)
|
|
}
|
|
if node.has_high {
|
|
w.expr(node.high)
|
|
}
|
|
}
|
|
ast.SizeOf, ast.IsRefType {
|
|
w.expr(node.expr)
|
|
w.mark_by_type(node.typ)
|
|
}
|
|
ast.StringInterLiteral {
|
|
w.uses_interp = true
|
|
w.exprs(node.exprs)
|
|
}
|
|
ast.SelectorExpr {
|
|
w.expr(node.expr)
|
|
if node.expr_type != 0 {
|
|
w.mark_by_type(node.expr_type)
|
|
if method := w.table.find_method(w.table.sym(node.expr_type), node.field_name) {
|
|
w.fn_by_name(method.fkey())
|
|
}
|
|
}
|
|
w.mark_by_type(node.typ)
|
|
w.or_block(node.or_block)
|
|
if node.has_hidden_receiver {
|
|
w.used_closures++
|
|
}
|
|
}
|
|
ast.SqlExpr {
|
|
w.expr(node.db_expr)
|
|
w.expr(node.or_expr)
|
|
w.expr(node.offset_expr)
|
|
w.expr(node.order_expr)
|
|
w.expr(node.limit_expr)
|
|
w.expr(node.where_expr)
|
|
w.mark_by_type(node.typ)
|
|
w.uses_orm = true
|
|
}
|
|
ast.StructInit {
|
|
if node.typ == 0 {
|
|
return
|
|
}
|
|
sym := w.table.sym(node.typ)
|
|
w.mark_by_sym(sym)
|
|
if !w.uses_memdup && sym.kind == .sum_type {
|
|
w.uses_memdup = true
|
|
}
|
|
if node.has_update_expr {
|
|
w.expr(node.update_expr)
|
|
}
|
|
for sif in node.init_fields {
|
|
w.expr(sif.expr)
|
|
}
|
|
}
|
|
ast.TypeOf {
|
|
w.expr(node.expr)
|
|
w.mark_by_type(node.typ)
|
|
}
|
|
///
|
|
ast.AsCast {
|
|
w.expr(node.expr)
|
|
w.fn_by_name('__as_cast')
|
|
w.fn_by_name('new_array_from_c_array')
|
|
w.mark_by_sym_name('VCastTypeIndexName')
|
|
}
|
|
ast.AtExpr {}
|
|
ast.BoolLiteral {}
|
|
ast.FloatLiteral {}
|
|
ast.CharLiteral {}
|
|
ast.IntegerLiteral {}
|
|
ast.StringLiteral {}
|
|
ast.CTempVar {
|
|
w.expr(node.orig)
|
|
}
|
|
ast.Comment {}
|
|
ast.EnumVal {
|
|
if e := w.table.enum_decls[node.enum_name] {
|
|
filtered := e.fields.filter(it.name == node.val)
|
|
if filtered.len != 0 && filtered[0].expr !is ast.EmptyExpr {
|
|
w.expr(filtered[0].expr)
|
|
}
|
|
}
|
|
w.mark_by_type(node.typ)
|
|
}
|
|
ast.LockExpr {
|
|
w.uses_lock = true
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.OffsetOf {}
|
|
ast.OrExpr {
|
|
w.or_block(node)
|
|
}
|
|
ast.SelectExpr {
|
|
for branch in node.branches {
|
|
w.stmt(branch.stmt)
|
|
w.stmts(branch.stmts)
|
|
}
|
|
w.uses_channel = true
|
|
}
|
|
ast.TypeNode {
|
|
w.mark_by_type(node.typ)
|
|
}
|
|
ast.UnsafeExpr {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.NodeError {}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) {
|
|
if node == unsafe { nil } {
|
|
return
|
|
}
|
|
if w.level == 0 {
|
|
last_is_builtin_mod := w.is_builtin_mod
|
|
w.is_builtin_mod = node.mod in ['builtin', 'os', 'strconv', 'builtin.closure']
|
|
defer {
|
|
w.is_builtin_mod = last_is_builtin_mod
|
|
}
|
|
}
|
|
w.table.used_features.used_attr_weak = w.table.used_features.used_attr_weak || node.is_weak
|
|
w.table.used_features.used_attr_noreturn = w.table.used_features.used_attr_noreturn
|
|
|| node.is_noreturn
|
|
if node.language == .c {
|
|
w.mark_fn_as_used(node.fkey())
|
|
w.mark_fn_ret_and_params(node.return_type, node.params)
|
|
return
|
|
}
|
|
|
|
fkey := node.fkey()
|
|
if w.used_fns[fkey] {
|
|
return
|
|
}
|
|
last_is_direct_array_access := w.is_direct_array_access
|
|
w.is_direct_array_access = node.is_direct_arr || w.pref.no_bounds_checking
|
|
defer { w.is_direct_array_access = last_is_direct_array_access }
|
|
if w.trace_enabled {
|
|
w.level++
|
|
defer { w.level-- }
|
|
receiver_name := if node.is_method && node.receiver.typ != 0 {
|
|
w.table.type_to_str(node.receiver.typ) + '.'
|
|
} else {
|
|
''
|
|
}
|
|
eprintln('>>>${' '.repeat(w.level)}${receiver_name}${node.name}')
|
|
}
|
|
if node.is_closure {
|
|
w.used_closures++
|
|
}
|
|
if node.no_body {
|
|
w.mark_fn_as_used(fkey)
|
|
return
|
|
}
|
|
if node.is_method {
|
|
w.mark_by_type(node.receiver.typ)
|
|
}
|
|
w.mark_fn_ret_and_params(node.return_type, node.params)
|
|
w.mark_fn_as_used(fkey)
|
|
w.stmts(node.stmts)
|
|
w.defer_stmts(node.defer_stmts)
|
|
}
|
|
|
|
pub fn (mut w Walker) call_expr(mut node ast.CallExpr) {
|
|
if node == unsafe { nil } {
|
|
return
|
|
}
|
|
for arg in node.args {
|
|
w.expr(arg.expr)
|
|
}
|
|
for concrete_type in node.concrete_types {
|
|
w.mark_by_type(concrete_type)
|
|
}
|
|
if node.language == .c {
|
|
if node.name in ['C.wyhash', 'C.wyhash64'] {
|
|
w.features.used_maps++
|
|
}
|
|
w.mark_by_type(node.return_type)
|
|
return
|
|
}
|
|
if node.is_method && node.left_type != 0 {
|
|
w.mark_by_type(node.left_type)
|
|
left_sym := w.table.sym(node.left_type)
|
|
if left_sym.info is ast.Aggregate {
|
|
for receiver_type in left_sym.info.types {
|
|
receiver_sym := w.table.sym(receiver_type)
|
|
if m := receiver_sym.find_method(node.name) {
|
|
fn_name := '${int(m.receiver_type)}.${node.name}'
|
|
if !w.used_fns[fn_name] {
|
|
w.fn_by_name(fn_name)
|
|
}
|
|
}
|
|
}
|
|
} else if left_sym.info is ast.Interface {
|
|
for typ in left_sym.info.types {
|
|
sym := w.table.sym(typ)
|
|
_, embed_types := w.table.find_method_from_embeds(sym, node.name) or {
|
|
ast.Fn{}, []ast.Type{}
|
|
}
|
|
if embed_types.len != 0 {
|
|
fn_embed := '${int(embed_types.last())}.${node.name}'
|
|
w.fn_by_name(fn_embed)
|
|
}
|
|
}
|
|
} else if node.from_embed_types.len != 0 && !node.left_type.has_flag(.generic) {
|
|
_, embed_types := w.table.find_method_from_embeds(w.table.final_sym(node.left_type),
|
|
node.name) or { ast.Fn{}, []ast.Type{} }
|
|
if embed_types.len != 0 {
|
|
fn_embed := '${int(embed_types.last())}.${node.name}'
|
|
w.fn_by_name(fn_embed)
|
|
}
|
|
} else {
|
|
match left_sym.info {
|
|
ast.Array, ast.ArrayFixed {
|
|
if !w.uses_arr_void && node.name in ['contains', 'index'] {
|
|
if w.table.final_sym(left_sym.info.elem_type).kind == .function {
|
|
w.uses_arr_void = true
|
|
}
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
}
|
|
w.expr(node.left)
|
|
w.or_block(node.or_block)
|
|
|
|
mut fn_name := node.fkey()
|
|
mut receiver_typ := node.receiver_type
|
|
if w.used_fns[fn_name] {
|
|
return
|
|
}
|
|
if node.is_method {
|
|
if node.left_type != 0 {
|
|
lsym := w.table.sym(node.left_type)
|
|
// Note: maps and arrays are implemented in `builtin` as concrete types `map` and `array`.
|
|
// They are not normal generics expanded, to separate structs, parametrized on the type of the element.
|
|
// All []Type or map[Type]Another types are typedefs to those `map` and `array` types, and all map and array methods
|
|
// are actually methods on the `builtin` concrete types.
|
|
match lsym.kind {
|
|
.array {
|
|
if !w.used_arr_method[node.name] {
|
|
w.mark_builtin_array_method_as_used(node.name)
|
|
w.used_arr_method[node.name] = true
|
|
}
|
|
}
|
|
.map {
|
|
if !w.used_map_method[node.name] {
|
|
w.mark_builtin_map_method_as_used(node.name)
|
|
w.used_map_method[node.name] = true
|
|
}
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
} else if node.is_fn_a_const {
|
|
const_fn_name := '${node.mod}.${fn_name}'
|
|
if const_fn_name in w.all_consts {
|
|
w.mark_const_as_used(const_fn_name)
|
|
}
|
|
} else if node.is_fn_var {
|
|
w.mark_global_as_used(node.name)
|
|
}
|
|
if node.is_method && node.receiver_type.has_flag(.generic) && node.receiver_concrete_type != 0
|
|
&& !node.receiver_concrete_type.has_flag(.generic) {
|
|
// if receiver is generic, then cgen requires `node.receiver_type` to be T.
|
|
// We therefore need to get the concrete type from `node.receiver_concrete_type`.
|
|
fn_name = '${int(node.receiver_concrete_type)}.${node.name}'
|
|
receiver_typ = node.receiver_concrete_type
|
|
}
|
|
w.mark_by_type(node.return_type)
|
|
mut stmt := w.all_fns[fn_name] or { return }
|
|
if !stmt.should_be_skipped && stmt.name == node.name {
|
|
if !node.is_method || receiver_typ == stmt.receiver.typ {
|
|
w.fn_decl(mut stmt)
|
|
}
|
|
if node.return_type.has_flag(.option) {
|
|
w.used_option++
|
|
} else if node.return_type.has_flag(.result) {
|
|
w.used_result++
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) fn_by_name(fn_name string) {
|
|
if w.used_fns[fn_name] {
|
|
return
|
|
}
|
|
mut stmt := w.all_fns[fn_name] or { return }
|
|
w.fn_decl(mut stmt)
|
|
}
|
|
|
|
pub fn (mut w Walker) struct_fields(sfields []ast.StructField) {
|
|
for sf in sfields {
|
|
if sf.has_default_expr {
|
|
w.expr(sf.default_expr)
|
|
}
|
|
if !w.uses_atomic && sf.typ.has_flag(.atomic_f) {
|
|
w.uses_atomic = true
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) const_fields(cfields []ast.ConstField) {
|
|
for cf in cfields {
|
|
w.expr(cf.expr)
|
|
}
|
|
}
|
|
|
|
@[inline]
|
|
pub fn (mut w Walker) or_block(node ast.OrExpr) {
|
|
if node.kind == .block {
|
|
w.stmts(node.stmts)
|
|
} else if node.kind == .propagate_option {
|
|
w.used_option++
|
|
w.used_panic++
|
|
} else if node.kind == .propagate_result {
|
|
w.used_result++
|
|
w.used_panic++
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_fn_ret_and_params(return_type ast.Type, params []ast.Param) {
|
|
if return_type != 0 {
|
|
if return_type.has_flag(.option) {
|
|
w.used_option++
|
|
} else if return_type.has_flag(.result) {
|
|
w.used_result++
|
|
}
|
|
w.mark_by_type(return_type.clear_option_and_result())
|
|
}
|
|
for param in params {
|
|
w.mark_by_type(param.typ)
|
|
}
|
|
}
|
|
|
|
@[inline]
|
|
pub fn (mut w Walker) mark_by_sym_name(name string) {
|
|
if sym := w.table.find_sym(name) {
|
|
w.mark_by_sym(sym)
|
|
}
|
|
}
|
|
|
|
@[inline]
|
|
pub fn (mut w Walker) mark_by_type(typ ast.Type) {
|
|
if typ == 0 || typ.has_flag(.generic) || typ in w.used_types {
|
|
return
|
|
}
|
|
w.mark_by_sym(w.table.sym(typ))
|
|
w.used_types[typ] = true
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) {
|
|
if isym.idx in w.used_syms {
|
|
return
|
|
}
|
|
w.used_syms[isym.idx] = true
|
|
match isym.info {
|
|
ast.Struct {
|
|
for ifield in isym.info.fields {
|
|
if ifield.has_default_expr {
|
|
w.expr(ifield.default_expr)
|
|
}
|
|
if ifield.typ != 0 {
|
|
fsym := w.table.sym(ifield.typ.idx())
|
|
if ifield.typ.has_flag(.option) {
|
|
w.used_option++
|
|
if !ifield.has_default_expr {
|
|
w.used_none++
|
|
}
|
|
}
|
|
w.mark_by_sym(fsym)
|
|
}
|
|
if !w.features.auto_str_ptr && ifield.typ.is_ptr()
|
|
&& isym.idx in w.features.print_types {
|
|
w.features.auto_str_ptr = true
|
|
}
|
|
}
|
|
for embed in isym.info.embeds {
|
|
w.mark_by_type(embed)
|
|
}
|
|
if decl := w.all_structs[isym.name] {
|
|
w.struct_fields(decl.fields)
|
|
w.uses_mem_align = w.uses_mem_align || decl.is_aligned
|
|
for iface_typ in decl.implements_types {
|
|
w.mark_by_type(iface_typ.typ)
|
|
}
|
|
}
|
|
}
|
|
ast.ArrayFixed, ast.Array {
|
|
if !w.uses_array && !w.is_direct_array_access {
|
|
w.uses_array = true
|
|
}
|
|
w.mark_by_type(isym.info.elem_type)
|
|
}
|
|
ast.SumType {
|
|
for typ in isym.info.variants {
|
|
if typ == ast.map_type {
|
|
w.features.used_maps++
|
|
continue
|
|
}
|
|
if typ.has_flag(.option) {
|
|
w.used_option++
|
|
}
|
|
sym := w.table.sym(typ)
|
|
w.mark_by_sym(sym)
|
|
w.uses_array_sumtype = w.uses_array_sumtype || sym.kind == .array
|
|
}
|
|
}
|
|
ast.Map {
|
|
w.mark_by_type(isym.info.key_type)
|
|
w.mark_by_type(isym.info.value_type)
|
|
w.features.used_maps++
|
|
if isym.info.value_type.has_flag(.option) {
|
|
w.used_option++
|
|
}
|
|
}
|
|
ast.Alias {
|
|
w.mark_by_type(isym.info.parent_type)
|
|
}
|
|
ast.FnType {
|
|
for param in isym.info.func.params {
|
|
w.mark_by_type(param.typ)
|
|
}
|
|
if isym.info.func.return_type != 0 {
|
|
w.mark_by_type(isym.info.func.return_type.clear_option_and_result())
|
|
}
|
|
}
|
|
ast.MultiReturn {
|
|
for typ in isym.info.types {
|
|
w.mark_by_type(typ)
|
|
}
|
|
}
|
|
ast.Chan {
|
|
w.uses_channel = true
|
|
w.mark_by_type(isym.info.elem_type)
|
|
}
|
|
ast.Aggregate {
|
|
for typ in isym.info.types {
|
|
w.mark_by_type(typ)
|
|
}
|
|
}
|
|
ast.Enum {
|
|
w.mark_by_type(isym.info.typ)
|
|
if enum_ := w.table.enum_decls[isym.name] {
|
|
for field in enum_.fields {
|
|
if field.has_expr {
|
|
w.expr(field.expr)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ast.Interface {
|
|
for typ in isym.info.types {
|
|
if typ == ast.map_type {
|
|
w.features.used_maps++
|
|
}
|
|
w.mark_by_type(typ)
|
|
for method in isym.info.methods {
|
|
for ityp in [typ.set_nr_muls(1), typ.set_nr_muls(0)] {
|
|
mname := int(ityp.clear_flags()).str() + '.' + method.name
|
|
w.fn_by_name(mname)
|
|
}
|
|
}
|
|
for embed_method in w.table.get_embed_methods(w.table.sym(typ)) {
|
|
mname := int(embed_method.params[0].typ.clear_flags()).str() + '.' +
|
|
embed_method.name
|
|
w.fn_by_name(mname)
|
|
}
|
|
}
|
|
for embed in isym.info.embeds {
|
|
w.mark_by_type(embed)
|
|
}
|
|
for generic_type in isym.info.generic_types {
|
|
w.mark_by_type(generic_type)
|
|
}
|
|
w.mark_by_type(isym.info.parent_type)
|
|
for field in isym.info.fields {
|
|
w.mark_by_type(field.typ)
|
|
}
|
|
for method in isym.methods {
|
|
w.mark_by_type(method.receiver_type)
|
|
w.mark_fn_ret_and_params(method.return_type, method.params)
|
|
}
|
|
}
|
|
ast.Thread {
|
|
w.mark_by_type(isym.info.return_type)
|
|
}
|
|
else {}
|
|
}
|
|
}
|
|
|
|
fn (mut w Walker) remove_unused_fn_generic_types() {
|
|
for _, node in w.all_fns {
|
|
mut count := 0
|
|
nkey := node.fkey()
|
|
if all_concrete_types := w.table.fn_generic_types[nkey] {
|
|
if all_concrete_types.len == 0 {
|
|
continue
|
|
}
|
|
for k, concrete_types in all_concrete_types {
|
|
if concrete_types.len != 1 {
|
|
continue
|
|
}
|
|
if concrete_types[0].idx() !in w.used_syms {
|
|
w.table.fn_generic_types[nkey].delete(k - count)
|
|
count++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn (mut w Walker) mark_resource_dependencies() {
|
|
string_idx_str := ast.string_type_idx.str()
|
|
array_idx_str := ast.array_type_idx.str()
|
|
|
|
if w.trace_enabled {
|
|
eprintln('>>>>>>>>>> DEPS USAGE')
|
|
}
|
|
if w.features.dump {
|
|
w.fn_by_name('eprint')
|
|
w.fn_by_name('eprintln')
|
|
builderptr_idx := int(w.table.find_type('strings.Builder').ref()).str()
|
|
w.fn_by_name(builderptr_idx + '.str')
|
|
w.fn_by_name(builderptr_idx + '.free')
|
|
w.fn_by_name(builderptr_idx + '.write_rune')
|
|
w.fn_by_name(builderptr_idx + '.write_string')
|
|
w.fn_by_name('strings.new_builder')
|
|
w.uses_free[ast.string_type] = true
|
|
|
|
if w.table.dumps.keys().any(ast.Type(u32(it)).has_flag(.option)) {
|
|
w.fn_by_name('str_intp')
|
|
}
|
|
}
|
|
if w.features.auto_str_ptr {
|
|
w.fn_by_name('isnil')
|
|
w.fn_by_name('tos4')
|
|
w.fn_by_name('str_intp')
|
|
}
|
|
if w.uses_channel {
|
|
w.fn_by_name('sync.new_channel_st')
|
|
w.fn_by_name('sync.channel_select')
|
|
}
|
|
if w.uses_lock {
|
|
w.mark_by_sym_name('sync.RwMutex')
|
|
}
|
|
if w.uses_orm {
|
|
w.fn_by_name('__new_array_with_default_noscan')
|
|
w.fn_by_name('new_array_from_c_array')
|
|
w.fn_by_name('__new_array')
|
|
w.fn_by_name('${ast.array_type_idx}.get')
|
|
w.fn_by_name(int(ast.array_type.ref()).str() + '.push')
|
|
}
|
|
if w.uses_ct_fields {
|
|
w.mark_by_sym_name('FieldData')
|
|
}
|
|
if w.uses_ct_methods {
|
|
w.mark_by_sym_name('FunctionData')
|
|
}
|
|
if w.uses_ct_params {
|
|
w.mark_by_sym_name('MethodParam')
|
|
}
|
|
if w.uses_ct_values {
|
|
w.mark_by_sym_name('EnumData')
|
|
}
|
|
if w.uses_ct_variants {
|
|
w.mark_by_sym_name('VariantData')
|
|
}
|
|
if w.uses_ct_attribute {
|
|
w.mark_by_sym_name('VAttribute')
|
|
}
|
|
if w.uses_map_update {
|
|
w.fn_by_name('new_map_update_init')
|
|
}
|
|
if w.uses_mem_align {
|
|
w.fn_by_name('memdup_align')
|
|
}
|
|
if w.uses_spawn {
|
|
w.fn_by_name('malloc')
|
|
w.fn_by_name('tos3')
|
|
}
|
|
if w.uses_memdup {
|
|
w.fn_by_name('memdup')
|
|
}
|
|
if w.uses_debugger {
|
|
w.mark_by_type(w.table.find_or_register_map(ast.string_type, ast.string_type))
|
|
}
|
|
if w.uses_arr_void {
|
|
w.mark_by_type(w.table.find_or_register_array(ast.voidptr_type))
|
|
}
|
|
if w.features.auto_str || w.uses_dump {
|
|
w.fn_by_name(ast.string_type_idx.str() + '.repeat')
|
|
w.fn_by_name('tos3')
|
|
}
|
|
if w.uses_index || w.pref.is_shared {
|
|
w.fn_by_name(array_idx_str + '.slice')
|
|
w.fn_by_name(array_idx_str + '.get')
|
|
}
|
|
if w.uses_str_index {
|
|
w.fn_by_name(string_idx_str + '.at')
|
|
if w.uses_str_index_check {
|
|
w.fn_by_name(string_idx_str + '.at_with_check')
|
|
}
|
|
if w.uses_str_range {
|
|
w.fn_by_name(string_idx_str + '.substr')
|
|
}
|
|
}
|
|
for typ, _ in w.table.used_features.print_types {
|
|
w.mark_by_type(typ)
|
|
}
|
|
if w.trace_enabled {
|
|
ptypes := w.table.used_features.print_types.keys().map(w.table.type_to_str(it))
|
|
eprintln('>>>>>>>>>> PRINT TYPES ${ptypes}')
|
|
stypes := w.uses_str.keys().filter(it != 0).map(w.table.type_to_str(it))
|
|
eprintln('>>>>>>>>>> USES .str() CALLS ON TYPES ${stypes}')
|
|
ftypes := w.uses_free.keys().map(w.table.type_to_str(it))
|
|
eprintln('>>>>>>>>>> USES .free() CALLS ON TYPES ${ftypes}')
|
|
}
|
|
if w.trace_enabled {
|
|
eprintln('>>>>>>>>>> ALL_FNS LOOP')
|
|
}
|
|
mut has_ptr_print := false
|
|
mut map_fns := map[string]ast.FnDecl{}
|
|
has_str_call := w.uses_interp || w.uses_asserts || w.uses_str.len > 0
|
|
|| w.features.print_types.len > 0
|
|
|
|
orm_impls := w.table.iface_types['orm.Connection'] or { []ast.Type{} }
|
|
for k, mut func in w.all_fns {
|
|
if has_str_call && k.ends_with('.str') {
|
|
if func.receiver.typ.idx() in w.used_syms {
|
|
w.fn_by_name(k)
|
|
if !has_ptr_print && func.receiver.typ.is_ptr() {
|
|
w.fn_by_name('ptr_str')
|
|
has_ptr_print = true
|
|
}
|
|
}
|
|
continue
|
|
}
|
|
if (w.pref.autofree || (w.uses_free.len > 0 && func.receiver.typ.idx() in w.used_syms))
|
|
&& k.ends_with('.free') {
|
|
w.fn_by_name(k)
|
|
continue
|
|
}
|
|
if w.uses_atomic && k.starts_with('_Atomic') {
|
|
w.fn_by_name(k)
|
|
continue
|
|
}
|
|
if func.name in ['+', '-', '*', '%', '/', '<', '=='] {
|
|
if func.receiver.typ.idx() in w.used_syms {
|
|
w.fn_by_name(k)
|
|
}
|
|
continue
|
|
}
|
|
if !func.is_static_type_method && func.receiver.typ != ast.void_type
|
|
&& func.generic_names.len > 0 {
|
|
if func.receiver.typ.set_nr_muls(0) in w.table.used_features.comptime_syms
|
|
|| func.receiver.typ in w.table.used_features.comptime_syms {
|
|
w.fn_by_name(k)
|
|
}
|
|
continue
|
|
}
|
|
if func.is_method && !func.receiver.typ.has_flag(.generic) && func.receiver.typ.is_ptr() {
|
|
method_receiver_typename := w.table.type_to_str(func.receiver.typ)
|
|
if method_receiver_typename in ['&map', '&mapnode', '&SortedMap', '&DenseArray'] {
|
|
map_fns[k] = func
|
|
}
|
|
continue
|
|
} else if k.starts_with('map_') {
|
|
map_fns[k] = func
|
|
continue
|
|
}
|
|
if orm_impls.len > 0 && k.starts_with('orm.') {
|
|
w.fn_by_name(k)
|
|
continue
|
|
}
|
|
}
|
|
if w.uses_guard || w.uses_index_check {
|
|
w.fn_by_name('error')
|
|
w.fn_by_name(array_idx_str + '.get_with_check')
|
|
}
|
|
if w.uses_append {
|
|
ref_array_idx_str := int(ast.array_type.ref()).str()
|
|
w.fn_by_name(ref_array_idx_str + '.push')
|
|
w.fn_by_name(ref_array_idx_str + '.push_many')
|
|
w.fn_by_name(ref_array_idx_str + '.push_many_noscan')
|
|
w.fn_by_name(ref_array_idx_str + '.push_noscan')
|
|
}
|
|
if w.uses_array {
|
|
if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] {
|
|
w.fn_by_name('__new_array_noscan')
|
|
w.fn_by_name('new_array_from_c_array_noscan')
|
|
w.fn_by_name('__new_array_with_multi_default_noscan')
|
|
w.fn_by_name('__new_array_with_array_default_noscan')
|
|
w.fn_by_name('__new_array_with_default_noscan')
|
|
}
|
|
w.fn_by_name('__new_array')
|
|
w.fn_by_name('new_array_from_c_array')
|
|
w.fn_by_name('__new_array_with_multi_default')
|
|
w.fn_by_name('__new_array_with_array_default')
|
|
w.fn_by_name('__new_array_with_default')
|
|
w.fn_by_name('__new_array_with_default_noscan')
|
|
w.fn_by_name(int(ast.array_type.ref()).str() + '.set')
|
|
w.fn_by_name('clone_static_to_depth')
|
|
}
|
|
if w.uses_array_sumtype {
|
|
w.fn_by_name('__new_array')
|
|
}
|
|
if w.uses_fixed_arr_int {
|
|
w.fn_by_name('v_fixed_index')
|
|
}
|
|
if w.uses_str_range_index {
|
|
w.fn_by_name(string_idx_str + '.substr')
|
|
}
|
|
if w.uses_arr_range_index {
|
|
w.fn_by_name(array_idx_str + '.slice')
|
|
}
|
|
if w.uses_range_index_check {
|
|
w.fn_by_name(string_idx_str + '.substr_with_check')
|
|
w.fn_by_name(array_idx_str + '.get_with_check')
|
|
}
|
|
if w.uses_str_range_index_gated {
|
|
w.fn_by_name(string_idx_str + '.substr_ni')
|
|
}
|
|
if w.uses_arr_range_index_gated {
|
|
w.fn_by_name(array_idx_str + '.slice_ni')
|
|
}
|
|
if w.uses_array || w.uses_arr_clone || w.uses_arr_sorted {
|
|
w.fn_by_name(array_idx_str + '.clone_static_to_depth')
|
|
}
|
|
// handle ORM drivers:
|
|
if orm_impls.len > 0 {
|
|
for orm_type in orm_impls {
|
|
typ := int(orm_type).str()
|
|
w.fn_by_name(typ + '.select')
|
|
w.fn_by_name(typ + '.insert')
|
|
w.fn_by_name(typ + '.update')
|
|
w.fn_by_name(typ + '.delete')
|
|
w.fn_by_name(typ + '.create')
|
|
w.fn_by_name(typ + '.drop')
|
|
w.fn_by_name(typ + '.last_id')
|
|
}
|
|
}
|
|
if w.features.used_maps == 0 && w.pref.autofree {
|
|
w.features.used_maps++
|
|
}
|
|
if w.features.used_maps > 0 {
|
|
w.fn_by_name('new_map')
|
|
w.fn_by_name('new_map_init')
|
|
w.fn_by_name('map_hash_string')
|
|
|
|
if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] {
|
|
w.fn_by_name('new_map_noscan_key')
|
|
w.fn_by_name('new_map_noscan_value')
|
|
w.fn_by_name('new_map_noscan_key_value')
|
|
w.fn_by_name('new_map_init_noscan_key')
|
|
w.fn_by_name('new_map_init_noscan_value')
|
|
w.fn_by_name('new_map_init_noscan_key_value')
|
|
}
|
|
for _, mut func in map_fns {
|
|
if !func.is_method {
|
|
w.fn_decl(mut func)
|
|
} else {
|
|
method_receiver_typename := w.table.type_to_str(func.receiver.typ)
|
|
if method_receiver_typename in ['&map', '&DenseArray'] {
|
|
w.fn_decl(mut func)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for k, func in map_fns {
|
|
if !func.is_method {
|
|
continue
|
|
}
|
|
w.used_fns.delete(k)
|
|
}
|
|
w.used_fns.delete('new_map')
|
|
w.used_fns.delete('new_map_init')
|
|
w.used_fns.delete('map_hash_string')
|
|
w.used_fns.delete('new_dense_array')
|
|
w.used_fns.delete('new_dense_array_noscan')
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) finalize(include_panic_deps bool) {
|
|
w.mark_resource_dependencies()
|
|
if w.trace_enabled {
|
|
eprintln('>>>>>>>>>> FINALIZE')
|
|
}
|
|
if w.uses_asserts {
|
|
w.fn_by_name('__print_assert_failure')
|
|
w.fn_by_name('isnil')
|
|
w.mark_by_sym_name('VAssertMetaInfo')
|
|
}
|
|
if w.used_panic > 0 {
|
|
w.fn_by_name('panic_option_not_set')
|
|
w.fn_by_name('panic_result_not_set')
|
|
}
|
|
if w.used_none > 0 || w.table.used_features.auto_str {
|
|
w.fn_by_name('_option_none')
|
|
w.mark_by_sym_name('_option')
|
|
}
|
|
if w.used_option > 0 {
|
|
w.fn_by_name('_option_clone')
|
|
w.fn_by_name('_option_ok')
|
|
w.mark_by_sym_name('_option')
|
|
}
|
|
if w.used_result > 0 {
|
|
w.fn_by_name('_result_ok')
|
|
w.mark_by_sym_name('_result')
|
|
}
|
|
if (w.used_option + w.used_result + w.used_none) > 0 {
|
|
w.mark_const_as_used('none__')
|
|
}
|
|
if include_panic_deps || w.uses_external_type || w.uses_asserts || w.uses_debugger
|
|
|| w.uses_interp {
|
|
if w.trace_enabled {
|
|
eprintln('>>>>> PANIC DEPS ${include_panic_deps} | external_type=${w.uses_external_type} | asserts=${w.uses_asserts} | dbg=${w.uses_debugger} interp=${w.uses_interp}')
|
|
}
|
|
ref_array_idx_str := int(ast.array_type.ref()).str()
|
|
string_idx_str := ast.string_type_idx.str()
|
|
|
|
if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] {
|
|
w.fn_by_name('__new_array_with_default_noscan')
|
|
w.fn_by_name(ref_array_idx_str + '.push_noscan')
|
|
}
|
|
w.fn_by_name('str_intp')
|
|
w.fn_by_name('__new_array_with_default')
|
|
w.fn_by_name(ref_array_idx_str + '.push')
|
|
w.fn_by_name(string_idx_str + '.substr')
|
|
w.fn_by_name('v_fixed_index')
|
|
w.mark_by_sym_name('StrIntpData')
|
|
w.mark_by_sym_name('StrIntpMem')
|
|
}
|
|
if w.uses_eq {
|
|
w.fn_by_name('fast_string_eq')
|
|
}
|
|
|
|
// remove unused symbols
|
|
w.remove_unused_fn_generic_types()
|
|
|
|
if w.trace_enabled {
|
|
syms := w.used_syms.keys().map(w.table.type_to_str(it))
|
|
eprintln('>>>>>>>>>> USED SYMS ${syms}')
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_generic_types() {
|
|
if w.trace_enabled {
|
|
eprintln('>>>>>>>>>> COMPTIME SYMS+CALLS')
|
|
}
|
|
for k, _ in w.table.used_features.comptime_calls {
|
|
w.fn_by_name(k)
|
|
}
|
|
|
|
for k, _ in w.table.used_features.comptime_syms {
|
|
sym := w.table.sym(k)
|
|
w.mark_by_sym(sym)
|
|
for method in sym.get_methods() {
|
|
w.fn_by_name('${k}.${method.name}')
|
|
}
|
|
}
|
|
}
|