-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathescapes.go
137 lines (113 loc) · 2.77 KB
/
escapes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
* Copyright (c) 2023-present Maxim Geraskin
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package escapes
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"unsafe"
)
//go:noinline
func ReturnValueParamAddress(escapesToHeap Point) *Point {
if escapesToHeap.X < 0 {
return nil
}
return &escapesToHeap
}
func Slices() int {
doesNotEscape := make([]byte, 10000)
escapes := make([]byte, 100000)
return len(doesNotEscape) + len(escapes)
}
// `y`: leaking param
func YIfLongest(x, y *string) *string {
if len(*y) > len(*x) {
return y
}
// `s`: escapes to heap
s := ""
return &s
}
//go:noinline
func YIfLongest_noinline(x, y *string) *string {
if len(*y) > len(*x) {
return y
}
s := ""
return &s
}
type Point struct {
X, Y int
}
func ReturnPointerParam(leaking *Point) *Point {
return leaking
}
// `p` neither leaks nor escapes
func ReturnPointerParamField(p *Point) int {
return p.X
}
func ReturnSlice(leaking []byte) []byte {
return leaking
}
// `p` does NOT leak
func SliceLen(p []byte) int {
return len(p)
}
// ***** Leaking examples **********************************************************************************************
// `a` escapes to heap because of the ReturnSlice() leaking param
func CallReturnSlice() {
a := make([]byte, 8)
fmt.Println(ReturnSlice(a))
}
// `a` is kept on the stack
func CallSliceLen(f func([]byte) int) {
a := make([]byte, 8)
// Result of SliceLen(a) escapes to heap
fmt.Println(SliceLen(a))
}
// ***** Closures ******************************************************************************************************
// `v` and `closure` escape
func ProvideClosure(closureCaller func(func() int) int) int {
var v int
closure := func() int {
v++
return 2
}
return closureCaller(closure)
}
type IClosure interface {
Do() int
ProvideInterfaceMethodAsClosure(closureCaller func(func() int) int) int
ProvideFieldAsClosure(func(func() int) int) int
}
type Closure struct {
v int
closure func() int
}
func (c *Closure) Do() int {
c.v++
return 2
}
// c.Do escapes
func (c *Closure) ProvideInterfaceMethodAsClosure(closureCaller func(func() int) int) int {
return closureCaller(c.Do)
}
// c.closure does NOT escape
func (c *Closure) ProvideFieldAsClosure(closureCaller func(func() int) int) int {
return closureCaller(c.closure)
}
// ***** binary.Read ******************************************************************************************************
func ReadInt64UsingBinaryRead(r io.Reader) (int64, error) {
var v int64
err := binary.Read(r, binary.BigEndian, &v)
return v, err
}
func ReadInt64(buf *bytes.Buffer) (res int64, err error) {
res, err = int64(binary.BigEndian.Uint64(buf.Next(int(unsafe.Sizeof(res))))), nil
return
}