Skip to content

Commit 3406415

Browse files
committed
Optimize method calls and builtins opcodes
1 parent 5ac99ca commit 3406415

File tree

12 files changed

+273
-150
lines changed

12 files changed

+273
-150
lines changed

ast/node.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ type NilNode struct {
4848

4949
type IdentifierNode struct {
5050
base
51-
Value string
52-
Deref bool
53-
Index []int
51+
Value string
52+
Deref bool
53+
FieldIndex []int
54+
Method bool
55+
MethodIndex int
5456
}
5557

5658
type IntegerNode struct {
@@ -105,12 +107,14 @@ type ChainNode struct {
105107

106108
type MemberNode struct {
107109
base
108-
Node Node
109-
Property Node
110-
Optional bool
111-
Deref bool
112-
Index []int
113-
Name string
110+
Node Node
111+
Property Node
112+
Name string
113+
Optional bool
114+
Deref bool
115+
FieldIndex []int
116+
Method bool
117+
MethodIndex int
114118
}
115119

116120
type SliceNode struct {

bench_test.go

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -129,21 +129,36 @@ func Benchmark_accessMap(b *testing.B) {
129129
}
130130
}
131131

132-
func Benchmark_call(b *testing.B) {
133-
type Env struct {
134-
Fn func(string, string, string) bool
135-
}
132+
type CallEnv struct {
133+
A int
134+
B int
135+
C int
136+
Fn func() bool
137+
FnFast func(...interface{}) interface{}
138+
Foo CallFoo
139+
}
140+
141+
func (CallEnv) Func() string {
142+
return "func"
143+
}
144+
145+
type CallFoo struct {
146+
D int
147+
E int
148+
F int
149+
}
150+
151+
func (CallFoo) Method() string {
152+
return "method"
153+
}
136154

137-
program, err := expr.Compile(`Fn("a", "b", "ab")`, expr.Env(Env{}))
155+
func Benchmark_callFunc(b *testing.B) {
156+
program, err := expr.Compile(`Func()`, expr.Env(CallEnv{}))
138157
if err != nil {
139158
b.Fatal(err)
140159
}
141160

142-
env := Env{
143-
Fn: func(s1, s2, s3 string) bool {
144-
return s1+s2 == s3
145-
},
146-
}
161+
env := CallEnv{}
147162

148163
for n := 0; n < b.N; n++ {
149164
_, err = vm.Run(program, env)
@@ -154,19 +169,32 @@ func Benchmark_call(b *testing.B) {
154169
}
155170
}
156171

157-
func Benchmark_callFast(b *testing.B) {
158-
type Env struct {
159-
Fn func(...interface{}) interface{}
172+
func Benchmark_callMethod(b *testing.B) {
173+
program, err := expr.Compile(`Foo.Method()`, expr.Env(CallEnv{}))
174+
if err != nil {
175+
b.Fatal(err)
176+
}
177+
178+
env := CallEnv{}
179+
180+
for n := 0; n < b.N; n++ {
181+
_, err = vm.Run(program, env)
182+
}
183+
184+
if err != nil {
185+
b.Fatal(err)
160186
}
187+
}
161188

162-
program, err := expr.Compile(`Fn("a", "b", "ab")`, expr.Env(Env{}))
189+
func Benchmark_callField(b *testing.B) {
190+
program, err := expr.Compile(`Fn()`, expr.Env(CallEnv{}))
163191
if err != nil {
164192
b.Fatal(err)
165193
}
166194

167-
env := Env{
168-
Fn: func(s ...interface{}) interface{} {
169-
return s[0].(string)+s[1].(string) == s[2].(string)
195+
env := CallEnv{
196+
Fn: func() bool {
197+
return true
170198
},
171199
}
172200

@@ -179,16 +207,39 @@ func Benchmark_callFast(b *testing.B) {
179207
}
180208
}
181209

182-
func Benchmark_callConstExpr(b *testing.B) {
183-
env := map[string]interface{}{
184-
"Fn": func(s ...interface{}) interface{} { return s[0].(string)+s[1].(string) == s[2].(string) },
210+
func Benchmark_callFast(b *testing.B) {
211+
program, err := expr.Compile(`FnFast()`, expr.Env(CallEnv{}))
212+
if err != nil {
213+
b.Fatal(err)
214+
}
215+
216+
env := CallEnv{
217+
FnFast: func(s ...interface{}) interface{} {
218+
return "fn_fast"
219+
},
220+
}
221+
222+
var out interface{}
223+
for n := 0; n < b.N; n++ {
224+
out, err = vm.Run(program, env)
225+
}
226+
227+
if err != nil {
228+
b.Fatal(err)
185229
}
230+
if out.(string) != "fn_fast" {
231+
b.Fail()
232+
}
233+
}
186234

187-
program, err := expr.Compile(`Fn("a", "b", "ab")`, expr.Env(env), expr.ConstExpr("Fn"))
235+
func Benchmark_callConstExpr(b *testing.B) {
236+
program, err := expr.Compile(`Func()`, expr.Env(CallEnv{}), expr.ConstExpr("Func"))
188237
if err != nil {
189238
b.Fatal(err)
190239
}
191240

241+
env := CallEnv{}
242+
192243
var out interface{}
193244
for n := 0; n < b.N; n++ {
194245
out, err = vm.Run(program, env)
@@ -197,7 +248,7 @@ func Benchmark_callConstExpr(b *testing.B) {
197248
if err != nil {
198249
b.Fatal(err)
199250
}
200-
if !out.(bool) {
251+
if out.(string) != "func" {
201252
b.Fail()
202253
}
203254
}

checker/checker.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ func (v *visitor) IdentifierNode(node *ast.IdentifierNode) (reflect.Type, info)
133133
}
134134
d, c := deref(t.Type)
135135
node.Deref = c
136-
if !t.Method {
137-
node.Index = t.Index
138-
}
136+
node.Method = t.Method
137+
node.MethodIndex = t.MethodIndex
138+
node.FieldIndex = t.FieldIndex
139139
return d, info{method: t.Method}
140140
}
141141
if !v.config.Strict {
@@ -367,6 +367,9 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
367367
// First, check methods defined on base type itself,
368368
// independent of which type it is. Without dereferencing.
369369
if m, ok := base.MethodByName(name.Value); ok {
370+
node.Method = true
371+
node.MethodIndex = m.Index
372+
node.Name = name.Value
370373
if base.Kind() == reflect.Interface {
371374
// In case of interface type method will not have a receiver,
372375
// and to prevent checker decreasing numbers of in arguments
@@ -408,7 +411,7 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
408411
if field, ok := fetchField(base, propertyName); ok {
409412
t, c := deref(field.Type)
410413
node.Deref = c
411-
node.Index = field.Index
414+
node.FieldIndex = field.Index
412415
node.Name = propertyName
413416
return t, info{}
414417
}

checker/checker_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import (
1010
"github.com/antonmedv/expr"
1111
"github.com/antonmedv/expr/ast"
1212
"github.com/antonmedv/expr/checker"
13-
"github.com/antonmedv/expr/checker/mock"
1413
"github.com/antonmedv/expr/conf"
1514
"github.com/antonmedv/expr/parser"
15+
"github.com/antonmedv/expr/test/mock"
1616
"github.com/stretchr/testify/assert"
1717
"github.com/stretchr/testify/require"
1818
)
@@ -77,7 +77,8 @@ var successTests = []string{
7777
"Foo.Bar == MapOfAny.id.Bar",
7878
"Foo.Bar.Baz == ''",
7979
"MapOfFoo['any'].Bar.Baz == ''",
80-
"Func(Foo) > 1",
80+
"Func() == 0",
81+
"FuncFoo(Foo) > 1",
8182
"Any() > 0",
8283
"Embed.EmbedString == ''",
8384
"EmbedString == ''",

0 commit comments

Comments
 (0)