mirror of
https://github.com/golang/go
synced 2024-11-26 19:41:19 -07:00
8f694f6661
single frame and non-overlapping variables reuse frame slots. As a result, entering and exiting blocks no longer requires code execution, which means jumps across block boundaries should be doable now. Frame slot initialization happens at definition time now, instead of at frame creation time. As an added bonus, Scope's are now exclusively compile-time objects and we no longer need to specially track the function activation frame for access to out vars. R=rsc APPROVED=rsc DELTA=313 (102 added, 90 deleted, 121 changed) OCL=32416 CL=32420
124 lines
2.5 KiB
Go
124 lines
2.5 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package eval
|
|
|
|
import (
|
|
"eval";
|
|
"fmt";
|
|
"log";
|
|
)
|
|
|
|
func (b *block) enterChild() *block {
|
|
if b.inner != nil {
|
|
log.Crash("Failed to exit child block before entering another child");
|
|
}
|
|
sub := &block{
|
|
outer: b,
|
|
scope: b.scope,
|
|
defs: make(map[string] Def),
|
|
offset: b.offset+b.numVars,
|
|
};
|
|
b.inner = sub;
|
|
return sub;
|
|
}
|
|
|
|
func (b *block) exit() {
|
|
if b.outer == nil {
|
|
log.Crash("Cannot exit top-level block");
|
|
}
|
|
if b.outer.inner != b {
|
|
log.Crash("Already exited block");
|
|
}
|
|
if b.inner != nil {
|
|
log.Crash("Exit of parent block without exit of child block");
|
|
}
|
|
b.outer.inner = nil;
|
|
}
|
|
|
|
func (b *block) ChildScope() *Scope {
|
|
if b.inner != nil {
|
|
log.Crash("Failed to exit child block before entering a child scope");
|
|
}
|
|
sub := b.enterChild();
|
|
sub.offset = 0;
|
|
sub.scope = &Scope{sub, 0};
|
|
return sub.scope;
|
|
}
|
|
|
|
func (b *block) DefineVar(name string, t Type) *Variable {
|
|
if _, ok := b.defs[name]; ok {
|
|
return nil;
|
|
}
|
|
v := b.DefineTemp(t);
|
|
if v != nil {
|
|
b.defs[name] = v;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
func (b *block) DefineTemp(t Type) *Variable {
|
|
if b.inner != nil {
|
|
log.Crash("Failed to exit child block before defining variable");
|
|
}
|
|
index := b.offset+b.numVars;
|
|
v := &Variable{index, t};
|
|
b.numVars++;
|
|
if index+1 > b.scope.maxVars {
|
|
b.scope.maxVars = index+1;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
func (b *block) DefineConst(name string, t Type, v Value) *Constant {
|
|
if _, ok := b.defs[name]; ok {
|
|
return nil;
|
|
}
|
|
c := &Constant{t, v};
|
|
b.defs[name] = c;
|
|
return c;
|
|
}
|
|
|
|
func (b *block) DefineType(name string, t Type) Type {
|
|
if _, ok := b.defs[name]; ok {
|
|
return nil;
|
|
}
|
|
// We take the representative type of t because multiple
|
|
// levels of naming are useless.
|
|
nt := &NamedType{name, t.rep()};
|
|
b.defs[name] = nt;
|
|
return nt;
|
|
}
|
|
|
|
func (b *block) Lookup(name string) (level int, def Def) {
|
|
for b != nil {
|
|
if d, ok := b.defs[name]; ok {
|
|
return level, d;
|
|
}
|
|
if b.outer != nil && b.scope != b.outer.scope {
|
|
level++;
|
|
}
|
|
b = b.outer;
|
|
}
|
|
return 0, nil;
|
|
}
|
|
|
|
func (s *Scope) NewFrame(outer *Frame) *Frame {
|
|
return outer.child(s.maxVars);
|
|
}
|
|
|
|
func (f *Frame) Get(level int, index int) Value {
|
|
for ; level > 0; level-- {
|
|
f = f.Outer;
|
|
}
|
|
return f.Vars[index];
|
|
}
|
|
|
|
func (f *Frame) child(numVars int) *Frame {
|
|
// TODO(austin) This is probably rather expensive. All values
|
|
// require heap allocation and zeroing them when we execute a
|
|
// definition typically requires some computation.
|
|
return &Frame{f, make([]Value, numVars)};
|
|
}
|