432 lines
8.1 KiB
V
432 lines
8.1 KiB
V
// Copyright (c) 2019-2021 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
|
|
|
|
pub struct Walker {
|
|
pub mut:
|
|
table &ast.Table
|
|
used_fns map[string]bool // used_fns['println'] == true
|
|
used_consts map[string]bool // used_consts['os.args'] == true
|
|
n_maps int
|
|
n_asserts int
|
|
mut:
|
|
files []&ast.File
|
|
all_fns map[string]ast.FnDecl
|
|
all_consts map[string]ast.ConstField
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_fn_as_used(fkey string) {
|
|
$if trace_skip_unused_marked ? {
|
|
eprintln(' fn > |$fkey|')
|
|
}
|
|
w.used_fns[fkey] = true
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_const_as_used(ckey string) {
|
|
$if trace_skip_unused_marked ? {
|
|
eprintln(' const > |$ckey|')
|
|
}
|
|
w.used_consts[ckey] = true
|
|
cfield := w.all_consts[ckey] or { return }
|
|
w.expr(cfield.expr)
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) {
|
|
for fn_name in all_fn_root_names {
|
|
if fn_name !in w.used_fns {
|
|
$if trace_skip_unused_roots ? {
|
|
println('>>>> $fn_name uses: ')
|
|
}
|
|
w.fn_decl(mut w.all_fns[fn_name])
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) mark_exported_fns() {
|
|
for _, mut func in w.all_fns {
|
|
if func.is_exported {
|
|
w.fn_decl(mut func)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) stmt(node ast.Stmt) {
|
|
match mut node {
|
|
ast.EmptyStmt {}
|
|
ast.AsmStmt {
|
|
w.asm_io(node.output)
|
|
w.asm_io(node.input)
|
|
}
|
|
ast.AssertStmt {
|
|
if node.is_used {
|
|
w.expr(node.expr)
|
|
w.n_asserts++
|
|
}
|
|
}
|
|
ast.AssignStmt {
|
|
w.exprs(node.left)
|
|
w.exprs(node.right)
|
|
}
|
|
ast.Block {
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.CompFor {
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.ConstDecl {
|
|
w.const_fields(node.fields)
|
|
}
|
|
ast.ExprStmt {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.FnDecl {
|
|
w.fn_decl(mut node)
|
|
}
|
|
ast.ForCStmt {
|
|
w.expr(node.cond)
|
|
w.stmt(node.inc)
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.ForInStmt {
|
|
w.expr(node.cond)
|
|
w.expr(node.high)
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.ForStmt {
|
|
w.expr(node.cond)
|
|
w.stmts(node.stmts)
|
|
}
|
|
ast.Return {
|
|
w.exprs(node.exprs)
|
|
}
|
|
ast.SqlStmt {
|
|
w.expr(node.db_expr)
|
|
for line in node.lines {
|
|
w.expr(line.where_expr)
|
|
w.exprs(line.update_exprs)
|
|
}
|
|
}
|
|
ast.StructDecl {
|
|
w.struct_fields(node.fields)
|
|
}
|
|
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.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) {
|
|
match mut node {
|
|
ast.EmptyExpr {
|
|
// TODO make sure this doesn't happen
|
|
// panic('Walker: EmptyExpr')
|
|
}
|
|
ast.AnonFn {
|
|
w.fn_decl(mut node.decl)
|
|
}
|
|
ast.ArrayInit {
|
|
w.expr(node.len_expr)
|
|
w.expr(node.cap_expr)
|
|
w.expr(node.default_expr)
|
|
w.exprs(node.exprs)
|
|
}
|
|
ast.Assoc {
|
|
w.exprs(node.exprs)
|
|
}
|
|
ast.ArrayDecompose {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.CallExpr {
|
|
w.call_expr(mut node)
|
|
}
|
|
ast.CastExpr {
|
|
w.expr(node.expr)
|
|
w.expr(node.arg)
|
|
}
|
|
ast.ChanInit {
|
|
w.expr(node.cap_expr)
|
|
}
|
|
ast.ConcatExpr {
|
|
w.exprs(node.vals)
|
|
}
|
|
ast.ComptimeSelector {
|
|
w.expr(node.left)
|
|
w.expr(node.field_expr)
|
|
}
|
|
ast.ComptimeCall {
|
|
w.expr(node.left)
|
|
if node.is_vweb {
|
|
w.stmts(node.vweb_tmpl.stmts)
|
|
}
|
|
}
|
|
ast.DumpExpr {
|
|
w.expr(node.expr)
|
|
w.fn_by_name('eprint')
|
|
w.fn_by_name('eprintln')
|
|
}
|
|
ast.GoExpr {
|
|
w.expr(node.call_expr)
|
|
}
|
|
ast.IndexExpr {
|
|
w.expr(node.left)
|
|
w.expr(node.index)
|
|
w.or_block(node.or_expr)
|
|
}
|
|
ast.InfixExpr {
|
|
w.expr(node.left)
|
|
w.expr(node.right)
|
|
w.or_block(node.or_block)
|
|
if node.left_type == 0 {
|
|
return
|
|
}
|
|
sym := w.table.get_type_symbol(node.left_type)
|
|
if sym.kind == .struct_ {
|
|
if opmethod := sym.find_method(node.op.str()) {
|
|
w.fn_decl(mut &ast.FnDecl(opmethod.source_fn))
|
|
}
|
|
}
|
|
}
|
|
ast.IfGuardExpr {
|
|
w.expr(node.expr)
|
|
}
|
|
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)
|
|
}
|
|
else {
|
|
// `.unresolved`, `.blank_ident`, `.variable`, `.global`, `.function`
|
|
// println('>>> else, ast.Ident kind: $node.kind')
|
|
}
|
|
}
|
|
}
|
|
ast.Likely {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.MapInit {
|
|
w.exprs(node.keys)
|
|
w.exprs(node.vals)
|
|
w.n_maps++
|
|
}
|
|
ast.MatchExpr {
|
|
w.expr(node.cond)
|
|
for b in node.branches {
|
|
w.exprs(b.exprs)
|
|
w.stmts(b.stmts)
|
|
}
|
|
}
|
|
ast.None {}
|
|
ast.ParExpr {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.PrefixExpr {
|
|
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 {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.StringInterLiteral {
|
|
w.exprs(node.exprs)
|
|
}
|
|
ast.SelectorExpr {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.SqlExpr {
|
|
w.expr(node.db_expr)
|
|
w.expr(node.offset_expr)
|
|
w.expr(node.order_expr)
|
|
w.expr(node.limit_expr)
|
|
w.expr(node.where_expr)
|
|
}
|
|
ast.StructInit {
|
|
sym := w.table.get_type_symbol(node.typ)
|
|
if sym.kind == .struct_ {
|
|
info := sym.info as ast.Struct
|
|
for ifield in info.fields {
|
|
if ifield.has_default_expr {
|
|
w.expr(ifield.default_expr)
|
|
}
|
|
}
|
|
}
|
|
if node.has_update_expr {
|
|
w.expr(node.update_expr)
|
|
}
|
|
for sif in node.fields {
|
|
w.expr(sif.expr)
|
|
}
|
|
for sie in node.embeds {
|
|
w.expr(sie.expr)
|
|
}
|
|
}
|
|
ast.TypeOf {
|
|
w.expr(node.expr)
|
|
}
|
|
///
|
|
ast.AsCast {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.AtExpr {}
|
|
ast.BoolLiteral {}
|
|
ast.FloatLiteral {}
|
|
ast.CharLiteral {}
|
|
ast.IntegerLiteral {}
|
|
ast.StringLiteral {}
|
|
ast.CTempVar {
|
|
w.expr(node.orig)
|
|
}
|
|
ast.Comment {}
|
|
ast.EnumVal {}
|
|
ast.LockExpr {
|
|
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)
|
|
}
|
|
}
|
|
ast.TypeNode {}
|
|
ast.UnsafeExpr {
|
|
w.expr(node.expr)
|
|
}
|
|
ast.NodeError {}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) {
|
|
if node.language == .c {
|
|
return
|
|
}
|
|
fkey := if node.is_method { '${int(node.receiver.typ)}.$node.name' } else { node.name }
|
|
if w.used_fns[fkey] {
|
|
// This function is already known to be called, meaning it has been processed already.
|
|
// Save CPU time and do nothing.
|
|
return
|
|
}
|
|
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) {
|
|
for arg in node.args {
|
|
w.expr(arg.expr)
|
|
}
|
|
if node.language == .c {
|
|
return
|
|
}
|
|
w.expr(node.left)
|
|
w.or_block(node.or_block)
|
|
//
|
|
fn_name := if node.is_method { node.receiver_type.str() + '.' + node.name } else { node.name }
|
|
if w.used_fns[fn_name] {
|
|
return
|
|
}
|
|
w.mark_fn_as_used(fn_name)
|
|
stmt := w.all_fns[fn_name] or { return }
|
|
if stmt.name == node.name {
|
|
if !node.is_method || (node.receiver_type == stmt.receiver.typ) {
|
|
w.stmts(stmt.stmts)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) fn_by_name(fn_name string) {
|
|
if w.used_fns[fn_name] {
|
|
return
|
|
}
|
|
stmt := w.all_fns[fn_name] or { return }
|
|
w.mark_fn_as_used(fn_name)
|
|
w.stmts(stmt.stmts)
|
|
}
|
|
|
|
pub fn (mut w Walker) struct_fields(sfields []ast.StructField) {
|
|
for sf in sfields {
|
|
if sf.has_default_expr {
|
|
w.expr(sf.default_expr)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) const_fields(cfields []ast.ConstField) {
|
|
for cf in cfields {
|
|
w.expr(cf.expr)
|
|
}
|
|
}
|
|
|
|
pub fn (mut w Walker) or_block(node ast.OrExpr) {
|
|
if node.kind == .block {
|
|
w.stmts(node.stmts)
|
|
}
|
|
}
|