From fc4e3586c4a36b9567aafd4a2d8453a30960e662 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 11 Feb 2019 17:34:02 -0800 Subject: [PATCH] Fix a closure-block scope bug (#99) * fix issue #98 * remove objects.CompiledModule * remove objects.CompiledModule --- compiler/bytecode.go | 6 ----- compiler/symbol_table.go | 2 +- docs/objects.md | 2 +- objects/compiled_module.go | 45 ------------------------------------- runtime/vm_function_test.go | 34 ++++++++++++++++++++++++++++ runtime/vm_module_test.go | 38 +++++++++++++++++++++++++++++++ 6 files changed, 74 insertions(+), 53 deletions(-) delete mode 100644 objects/compiled_module.go diff --git a/compiler/bytecode.go b/compiler/bytecode.go index 56502f5..e0416b5 100644 --- a/compiler/bytecode.go +++ b/compiler/bytecode.go @@ -63,11 +63,6 @@ func (b *Bytecode) FormatConstants() (output []string) { for _, l := range FormatInstructions(cn.Instructions, 0) { output = append(output, fmt.Sprintf(" %s", l)) } - case *objects.CompiledModule: - output = append(output, fmt.Sprintf("[% 3d] (Compiled Module|%p)", cidx, &cn)) - for _, l := range FormatInstructions(cn.Instructions, 0) { - output = append(output, fmt.Sprintf(" %s", l)) - } default: output = append(output, fmt.Sprintf("[% 3d] %s (%s|%p)", cidx, cn, reflect.TypeOf(cn).Elem().Name(), &cn)) } @@ -116,5 +111,4 @@ func init() { gob.Register(&objects.MapIterator{}) gob.Register(&objects.ArrayIterator{}) gob.Register(&objects.Time{}) - gob.Register(&objects.CompiledModule{}) } diff --git a/compiler/symbol_table.go b/compiler/symbol_table.go index 5983cc9..da55a82 100644 --- a/compiler/symbol_table.go +++ b/compiler/symbol_table.go @@ -63,7 +63,7 @@ func (t *SymbolTable) Resolve(name string) (symbol *Symbol, depth int, ok bool) // if symbol is defined in parent table and if it's not global/builtin // then it's free variable. - if depth > 0 && symbol.Scope != ScopeGlobal && symbol.Scope != ScopeBuiltin { + if !t.block && depth > 0 && symbol.Scope != ScopeGlobal && symbol.Scope != ScopeBuiltin { return t.defineFree(symbol), depth, true } diff --git a/docs/objects.md b/docs/objects.md index b067e08..9e556d1 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -140,7 +140,7 @@ These are the basic types Tengo runtime supports out of the box: - [Iterators](https://godoc.org/github.com/d5/tengo/objects#Iterator): [StringIterator](https://godoc.org/github.com/d5/tengo/objects#StringIterator), [ArrayIterator](https://godoc.org/github.com/d5/tengo/objects#ArrayIterator), [MapIterator](https://godoc.org/github.com/d5/tengo/objects#MapIterator), [ImmutableMapIterator](https://godoc.org/github.com/d5/tengo/objects#ImmutableMapIterator) - [Error](https://godoc.org/github.com/d5/tengo/objects#Error) - [Undefined](https://godoc.org/github.com/d5/tengo/objects#Undefined) -- Other internal objects: [Closure](https://godoc.org/github.com/d5/tengo/objects#Closure), [CompiledModule](https://godoc.org/github.com/d5/tengo/objects#CompiledModule), [Break](https://godoc.org/github.com/d5/tengo/objects#Break), [Continue](https://godoc.org/github.com/d5/tengo/objects#Continue), [ReturnValue](https://godoc.org/github.com/d5/tengo/objects#ReturnValue) +- Other internal objects: [Closure](https://godoc.org/github.com/d5/tengo/objects#Closure), [Break](https://godoc.org/github.com/d5/tengo/objects#Break), [Continue](https://godoc.org/github.com/d5/tengo/objects#Continue), [ReturnValue](https://godoc.org/github.com/d5/tengo/objects#ReturnValue) See [Runtime Types](https://github.com/d5/tengo/blob/master/docs/runtime-types.md) for more details on these runtime types. diff --git a/objects/compiled_module.go b/objects/compiled_module.go deleted file mode 100644 index 76e0ea6..0000000 --- a/objects/compiled_module.go +++ /dev/null @@ -1,45 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// CompiledModule represents a compiled module. -type CompiledModule struct { - Instructions []byte // compiled instructions - NumGlobals int -} - -// TypeName returns the name of the type. -func (o *CompiledModule) TypeName() string { - return "compiled-module" -} - -func (o *CompiledModule) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *CompiledModule) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *CompiledModule) Copy() Object { - return &CompiledModule{ - Instructions: append([]byte{}, o.Instructions...), - NumGlobals: o.NumGlobals, - } -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *CompiledModule) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *CompiledModule) Equals(x Object) bool { - return false -} diff --git a/runtime/vm_function_test.go b/runtime/vm_function_test.go index 158bdc3..a0edee6 100644 --- a/runtime/vm_function_test.go +++ b/runtime/vm_function_test.go @@ -219,4 +219,38 @@ out = func() { }()`, 15) expectError(t, `return 5`) + + // closure and block scopes + expect(t, ` +func() { + a := 10 + func() { + b := 5 + if true { + out = a + 5 + } + }() +}()`, 15) + expect(t, ` +func() { + a := 10 + b := func() { return 5 } + func() { + if b() { + out = a + b() + } + }() +}()`, 15) + expect(t, ` +func() { + a := 10 + func() { + b := func() { return 5 } + func() { + if true { + out = a + b() + } + }() + }() +}()`, 15) } diff --git a/runtime/vm_module_test.go b/runtime/vm_module_test.go index f500ae1..2a55d53 100644 --- a/runtime/vm_module_test.go +++ b/runtime/vm_module_test.go @@ -208,3 +208,41 @@ export func() { "mod1": `export a`, }) } + +func TestModuleBlockScopes(t *testing.T) { + // block scopes in module + expectWithUserModules(t, `out = import("mod1")()`, 1, map[string]string{ + "mod1": ` + rand := import("rand") + foo := func() { return 1 } + export func() { + rand.intn(3) + return foo() + } + `, + }) + + expectWithUserModules(t, `out = import("mod1")()`, 10, map[string]string{ + "mod1": ` +rand := import("rand") +foo := func() { return 1 } +export func() { + rand.intn(3) + if foo() {} + return 10 +} +`, + }) + + expectWithUserModules(t, `out = import("mod1")()`, 10, map[string]string{ + "mod1": ` + rand := import("rand") + foo := func() { return 1 } + export func() { + rand.intn(3) + if true { foo() } + return 10 + } + `, + }) +}