github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/tools/golang_patched_dce/ad1d54f.diff (about)

     1  From ad1d54f498bd0f9de434773b6272d70f5d8ee980 Mon Sep 17 00:00:00 2001
     2  From: Brad Fitzpatrick <bradfitz@golang.org>
     3  Date: Fri, 06 Dec 2019 18:19:03 +0000
     4  Subject: [PATCH] text/template: avoid a global map to help the linker's deadcode elimination
     5  
     6  Fixes #36021
     7  Updates #2559
     8  Updates #26775
     9  
    10  Change-Id: I2e6708691311035b63866f25d5b4b3977a118290
    11  ---
    12  
    13  diff --git a/src/text/template/deadcode_test.go b/src/text/template/deadcode_test.go
    14  new file mode 100644
    15  index 0000000..7495f51
    16  --- /dev/null
    17  +++ b/src/text/template/deadcode_test.go
    18  @@ -0,0 +1,64 @@
    19  +// Copyright 2019 The Go Authors. All rights reserved.
    20  +// Use of this source code is governed by a BSD-style
    21  +// license that can be found in the LICENSE file.
    22  +
    23  +package template_test
    24  +
    25  +import (
    26  +	"bytes"
    27  +	"internal/testenv"
    28  +	"io/ioutil"
    29  +	"os"
    30  +	"os/exec"
    31  +	"path/filepath"
    32  +	"testing"
    33  +)
    34  +
    35  +// Issue 36021: verify that text/template doesn't prevent the linker from removing
    36  +// unused methods.
    37  +func TestDeadCodeElimination(t *testing.T) {
    38  +	if testing.Short() {
    39  +		t.Skip("skipping in short mode")
    40  +	}
    41  +	testenv.MustHaveGoBuild(t)
    42  +	const prog = `package main
    43  +
    44  +import (
    45  +	_ "text/template"
    46  +)
    47  +
    48  +type T struct{}
    49  +
    50  +func (t *T) Unused() { println("THIS SHOULD BE ELIMINATED") }
    51  +func (t *T) Used() {}
    52  +
    53  +var sink *T
    54  +
    55  +func main() {
    56  +	var t T
    57  +	sink = &t
    58  +	t.Used()
    59  +}
    60  +`
    61  +	td, err := ioutil.TempDir("", "text_template_TestDeadCodeElimination")
    62  +	if err != nil {
    63  +		t.Fatal(err)
    64  +	}
    65  +	defer os.Remove(td)
    66  +
    67  +	if err := ioutil.WriteFile(filepath.Join(td, "x.go"), []byte(prog), 0644); err != nil {
    68  +		t.Fatal(err)
    69  +	}
    70  +	cmd := exec.Command("go", "build", "-o", "x.exe", "x.go")
    71  +	cmd.Dir = td
    72  +	if out, err := cmd.CombinedOutput(); err != nil {
    73  +		t.Fatalf("go build: %v, %s", err, out)
    74  +	}
    75  +	slurp, err := ioutil.ReadFile(filepath.Join(td, "x.exe"))
    76  +	if err != nil {
    77  +		t.Fatal(err)
    78  +	}
    79  +	if bytes.Contains(slurp, []byte("THIS SHOULD BE ELIMINATED")) {
    80  +		t.Error("binary contains code that should be deadcode eliminated")
    81  +	}
    82  +}
    83  diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
    84  index 0568c79..55470d6 100644
    85  --- a/src/text/template/funcs.go
    86  +++ b/src/text/template/funcs.go
    87  @@ -12,6 +12,7 @@
    88   	"net/url"
    89   	"reflect"
    90   	"strings"
    91  +	"sync"
    92   	"unicode"
    93   	"unicode/utf8"
    94   )
    95  @@ -29,31 +30,49 @@
    96   // type can return interface{} or reflect.Value.
    97   type FuncMap map[string]interface{}
    98   
    99  -var builtins = FuncMap{
   100  -	"and":      and,
   101  -	"call":     call,
   102  -	"html":     HTMLEscaper,
   103  -	"index":    index,
   104  -	"slice":    slice,
   105  -	"js":       JSEscaper,
   106  -	"len":      length,
   107  -	"not":      not,
   108  -	"or":       or,
   109  -	"print":    fmt.Sprint,
   110  -	"printf":   fmt.Sprintf,
   111  -	"println":  fmt.Sprintln,
   112  -	"urlquery": URLQueryEscaper,
   113  +// builtins returns the FuncMap.
   114  +// It is not a global variable so the linker can dead code eliminate
   115  +// more when this isn't called. See golang.org/issue/36021.
   116  +// TODO: revert this back to a global map once golang.org/issue/2559 is fixed.
   117  +func builtins() FuncMap {
   118  +	return FuncMap{
   119  +		"and":      and,
   120  +		"call":     call,
   121  +		"html":     HTMLEscaper,
   122  +		"index":    index,
   123  +		"slice":    slice,
   124  +		"js":       JSEscaper,
   125  +		"len":      length,
   126  +		"not":      not,
   127  +		"or":       or,
   128  +		"print":    fmt.Sprint,
   129  +		"printf":   fmt.Sprintf,
   130  +		"println":  fmt.Sprintln,
   131  +		"urlquery": URLQueryEscaper,
   132   
   133  -	// Comparisons
   134  -	"eq": eq, // ==
   135  -	"ge": ge, // >=
   136  -	"gt": gt, // >
   137  -	"le": le, // <=
   138  -	"lt": lt, // <
   139  -	"ne": ne, // !=
   140  +		// Comparisons
   141  +		"eq": eq, // ==
   142  +		"ge": ge, // >=
   143  +		"gt": gt, // >
   144  +		"le": le, // <=
   145  +		"lt": lt, // <
   146  +		"ne": ne, // !=
   147  +	}
   148   }
   149   
   150  -var builtinFuncs = createValueFuncs(builtins)
   151  +var builtinFuncsOnce struct {
   152  +	sync.Once
   153  +	v map[string]reflect.Value
   154  +}
   155  +
   156  +// builtinFuncsOnce lazily computes & caches the builtinFuncs map.
   157  +// TODO: revert this back to a global map once golang.org/issue/2559 is fixed.
   158  +func builtinFuncs() map[string]reflect.Value {
   159  +	builtinFuncsOnce.Do(func() {
   160  +		builtinFuncsOnce.v = createValueFuncs(builtins())
   161  +	})
   162  +	return builtinFuncsOnce.v
   163  +}
   164   
   165   // createValueFuncs turns a FuncMap into a map[string]reflect.Value
   166   func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
   167  @@ -125,7 +144,7 @@
   168   			return fn, true
   169   		}
   170   	}
   171  -	if fn := builtinFuncs[name]; fn.IsValid() {
   172  +	if fn := builtinFuncs()[name]; fn.IsValid() {
   173   		return fn, true
   174   	}
   175   	return reflect.Value{}, false
   176  diff --git a/src/text/template/multi_test.go b/src/text/template/multi_test.go
   177  index 5769470..bf1f1b2 100644
   178  --- a/src/text/template/multi_test.go
   179  +++ b/src/text/template/multi_test.go
   180  @@ -242,7 +242,7 @@
   181   		t.Fatal(err)
   182   	}
   183   	// Add a new parse tree.
   184  -	tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins)
   185  +	tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins())
   186   	if err != nil {
   187   		t.Fatal(err)
   188   	}
   189  diff --git a/src/text/template/template.go b/src/text/template/template.go
   190  index 2c5ff01..e636907 100644
   191  --- a/src/text/template/template.go
   192  +++ b/src/text/template/template.go
   193  @@ -198,7 +198,7 @@
   194   func (t *Template) Parse(text string) (*Template, error) {
   195   	t.init()
   196   	t.muFuncs.RLock()
   197  -	trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
   198  +	trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins())
   199   	t.muFuncs.RUnlock()
   200   	if err != nil {
   201   		return nil, err