From ca128af33bd98af4a68f89ce523ae63aebc3d9ea Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Sun, 13 Jan 2019 14:24:39 -0800 Subject: [PATCH] reduce number of memory allocation in object binary operators --- objects/array.go | 3 ++ objects/builtin_convert.go | 8 +-- objects/float.go | 88 ++++++++++++++++++++++++------ objects/int.go | 106 ++++++++++++++++++++++++++++++------- objects/objects.go | 7 +++ objects/string.go | 3 ++ objects/undefined.go | 2 - runtime/vm.go | 34 ++++++------ 8 files changed, 193 insertions(+), 58 deletions(-) create mode 100644 objects/objects.go diff --git a/objects/array.go b/objects/array.go index 27663ce..5539a8e 100644 --- a/objects/array.go +++ b/objects/array.go @@ -29,6 +29,9 @@ func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) { if rhs, ok := rhs.(*Array); ok { switch op { case token.Add: + if len(rhs.Value) == 0 { + return o, nil + } return &Array{Value: append(o.Value, rhs.Value...)}, nil } } diff --git a/objects/builtin_convert.go b/objects/builtin_convert.go index c1d6cca..8a95ee1 100644 --- a/objects/builtin_convert.go +++ b/objects/builtin_convert.go @@ -14,7 +14,7 @@ func builtinString(args ...Object) (Object, error) { case *String: return arg, nil case *Undefined: - return undefined, nil + return UndefinedValue, nil default: return &String{Value: arg.String()}, nil } @@ -44,7 +44,7 @@ func builtinInt(args ...Object) (Object, error) { } } - return undefined, nil + return UndefinedValue, nil } func builtinFloat(args ...Object) (Object, error) { @@ -64,7 +64,7 @@ func builtinFloat(args ...Object) (Object, error) { } } - return undefined, nil + return UndefinedValue, nil } func builtinBool(args ...Object) (Object, error) { @@ -100,5 +100,5 @@ func builtinChar(args ...Object) (Object, error) { } } - return undefined, nil + return UndefinedValue, nil } diff --git a/objects/float.go b/objects/float.go index 1558e29..2d2d718 100644 --- a/objects/float.go +++ b/objects/float.go @@ -24,40 +24,96 @@ func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) { case *Float: switch op { case token.Add: - return &Float{o.Value + rhs.Value}, nil + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Sub: - return &Float{o.Value - rhs.Value}, nil + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Mul: - return &Float{o.Value * rhs.Value}, nil + r := o.Value * rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Quo: - return &Float{o.Value / rhs.Value}, nil + r := o.Value / rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Less: - return &Bool{o.Value < rhs.Value}, nil + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.Greater: - return &Bool{o.Value > rhs.Value}, nil + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.LessEq: - return &Bool{o.Value <= rhs.Value}, nil + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.GreaterEq: - return &Bool{o.Value >= rhs.Value}, nil + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil } case *Int: switch op { case token.Add: - return &Float{o.Value + float64(rhs.Value)}, nil + r := o.Value + float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Sub: - return &Float{o.Value - float64(rhs.Value)}, nil + r := o.Value - float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Mul: - return &Float{o.Value * float64(rhs.Value)}, nil + r := o.Value * float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Quo: - return &Float{o.Value / float64(rhs.Value)}, nil + r := o.Value / float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil case token.Less: - return &Bool{o.Value < float64(rhs.Value)}, nil + if o.Value < float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil case token.Greater: - return &Bool{o.Value > float64(rhs.Value)}, nil + if o.Value > float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil case token.LessEq: - return &Bool{o.Value <= float64(rhs.Value)}, nil + if o.Value <= float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil case token.GreaterEq: - return &Bool{o.Value >= float64(rhs.Value)}, nil + if o.Value >= float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil } } diff --git a/objects/int.go b/objects/int.go index fb583c6..ed1a033 100644 --- a/objects/int.go +++ b/objects/int.go @@ -23,35 +23,91 @@ func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) { case *Int: switch op { case token.Add: - return &Int{o.Value + rhs.Value}, nil + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Sub: - return &Int{o.Value - rhs.Value}, nil + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Mul: - return &Int{o.Value * rhs.Value}, nil + r := o.Value * rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Quo: - return &Int{o.Value / rhs.Value}, nil + r := o.Value / rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Rem: - return &Int{o.Value % rhs.Value}, nil + r := o.Value % rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.And: - return &Int{o.Value & rhs.Value}, nil + r := o.Value & rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Or: - return &Int{o.Value | rhs.Value}, nil + r := o.Value | rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Xor: - return &Int{o.Value ^ rhs.Value}, nil + r := o.Value ^ rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.AndNot: - return &Int{o.Value &^ rhs.Value}, nil + r := o.Value &^ rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Shl: - return &Int{o.Value << uint(rhs.Value)}, nil + r := o.Value << uint64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Shr: - return &Int{o.Value >> uint(rhs.Value)}, nil + r := o.Value >> uint64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil case token.Less: - return &Bool{o.Value < rhs.Value}, nil + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.Greater: - return &Bool{o.Value > rhs.Value}, nil + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.LessEq: - return &Bool{o.Value <= rhs.Value}, nil + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.GreaterEq: - return &Bool{o.Value >= rhs.Value}, nil + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil } case *Float: switch op { @@ -64,13 +120,25 @@ func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) { case token.Quo: return &Float{float64(o.Value) / rhs.Value}, nil case token.Less: - return &Bool{float64(o.Value) < rhs.Value}, nil + if float64(o.Value) < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.Greater: - return &Bool{float64(o.Value) > rhs.Value}, nil + if float64(o.Value) > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.LessEq: - return &Bool{float64(o.Value) <= rhs.Value}, nil + if float64(o.Value) <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil case token.GreaterEq: - return &Bool{float64(o.Value) >= rhs.Value}, nil + if float64(o.Value) >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil } } diff --git a/objects/objects.go b/objects/objects.go new file mode 100644 index 0000000..c7bb942 --- /dev/null +++ b/objects/objects.go @@ -0,0 +1,7 @@ +package objects + +var ( + TrueValue Object = &Bool{Value: true} + FalseValue Object = &Bool{Value: false} + UndefinedValue Object = &Undefined{} +) diff --git a/objects/string.go b/objects/string.go index 672bdbb..382bedc 100644 --- a/objects/string.go +++ b/objects/string.go @@ -23,6 +23,9 @@ func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) { case *String: switch op { case token.Add: + if rhs.Value == "" { + return o, nil + } return &String{Value: o.Value + rhs.Value}, nil } case *Char: diff --git a/objects/undefined.go b/objects/undefined.go index 4d69d07..44802ae 100644 --- a/objects/undefined.go +++ b/objects/undefined.go @@ -2,8 +2,6 @@ package objects import "github.com/d5/tengo/compiler/token" -var undefined = &Undefined{} - type Undefined struct{} func (o Undefined) TypeName() string { diff --git a/runtime/vm.go b/runtime/vm.go index 0ec72d1..66d11ea 100644 --- a/runtime/vm.go +++ b/runtime/vm.go @@ -15,9 +15,9 @@ const ( ) var ( - trueObj objects.Object = &objects.Bool{Value: true} - falseObj objects.Object = &objects.Bool{Value: false} - undefinedObj objects.Object = &objects.Undefined{} + truePtr = &objects.TrueValue + falsePtr = &objects.FalseValue + undefinedPtr = &objects.UndefinedValue builtinFuncs []objects.Object ) @@ -83,7 +83,7 @@ func (v *VM) Run() error { } case compiler.OpNull: - if err := v.push(&undefinedObj); err != nil { + if err := v.push(undefinedPtr); err != nil { return err } case compiler.OpAdd: @@ -223,11 +223,11 @@ func (v *VM) Run() error { left := v.pop() if (*right).Equals(*left) { - if err := v.push(&trueObj); err != nil { + if err := v.push(truePtr); err != nil { return err } } else { - if err := v.push(&falseObj); err != nil { + if err := v.push(falsePtr); err != nil { return err } } @@ -236,11 +236,11 @@ func (v *VM) Run() error { left := v.pop() if (*right).Equals(*left) { - if err := v.push(&falseObj); err != nil { + if err := v.push(falsePtr); err != nil { return err } } else { - if err := v.push(&trueObj); err != nil { + if err := v.push(truePtr); err != nil { return err } } @@ -271,22 +271,22 @@ func (v *VM) Run() error { case compiler.OpPop: _ = v.pop() case compiler.OpTrue: - if err := v.push(&trueObj); err != nil { + if err := v.push(truePtr); err != nil { return err } case compiler.OpFalse: - if err := v.push(&falseObj); err != nil { + if err := v.push(falsePtr); err != nil { return err } case compiler.OpLNot: operand := v.pop() if (*operand).IsFalsy() { - if err := v.push(&trueObj); err != nil { + if err := v.push(truePtr); err != nil { return err } } else { - if err := v.push(&falseObj); err != nil { + if err := v.push(falsePtr); err != nil { return err } } @@ -528,7 +528,7 @@ func (v *VM) Run() error { v.sp = frame.basePointer - 1 - if err := v.push(&undefinedObj); err != nil { + if err := v.push(undefinedPtr); err != nil { return err } @@ -672,11 +672,11 @@ func (v *VM) Run() error { iterator := v.pop() b := (*iterator).(objects.Iterator).Next() if b { - if err := v.push(&trueObj); err != nil { + if err := v.push(truePtr); err != nil { return err } } else { - if err := v.push(&falseObj); err != nil { + if err := v.push(falsePtr); err != nil { return err } } @@ -783,7 +783,7 @@ func (v *VM) executeArrayIndex(arr *objects.Array, index int64) error { } func (v *VM) executeMapIndex(map_ *objects.Map, key string) error { - var res = undefinedObj + var res = objects.UndefinedValue val, ok := map_.Value[key] if ok { res = val @@ -974,7 +974,7 @@ func (v *VM) callCallable(callable objects.Callable, numArgs int) error { // nil return -> undefined if res == nil { - res = undefinedObj + res = objects.UndefinedValue } return v.push(&res)