github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/frame.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package grumpy
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  )
    21  
    22  // RunState represents the current point of execution within a Python function.
    23  type RunState int
    24  
    25  const (
    26  	notBaseExceptionMsg = "exceptions must be derived from BaseException, not %q"
    27  )
    28  
    29  // Frame represents Python 'frame' objects.
    30  type Frame struct {
    31  	Object
    32  	*threadState
    33  	back *Frame `attr:"f_back"`
    34  	// checkpoints holds RunState values that should be executed when
    35  	// unwinding the stack due to an exception. Examples of checkpoints
    36  	// include exception handlers and finally blocks.
    37  	checkpoints []RunState
    38  	state       RunState
    39  	globals     *Dict `attr:"f_globals"`
    40  	lineno      int   `attr:"f_lineno"`
    41  	code        *Code `attr:"f_code"`
    42  	taken       bool
    43  }
    44  
    45  // NewRootFrame creates a Frame that is the bottom of a new stack.
    46  func NewRootFrame() *Frame {
    47  	f := &Frame{Object: Object{typ: FrameType}}
    48  	f.pushFrame(nil)
    49  	return f
    50  }
    51  
    52  // newChildFrame creates a new Frame whose parent frame is back.
    53  func newChildFrame(back *Frame) *Frame {
    54  	f := back.frameCache
    55  	if f == nil {
    56  		f = &Frame{Object: Object{typ: FrameType}}
    57  	} else {
    58  		back.frameCache, f.back = f.back, nil
    59  		// Reset local state late.
    60  		f.checkpoints = f.checkpoints[:0]
    61  		f.state = 0
    62  		f.lineno = 0
    63  	}
    64  	f.pushFrame(back)
    65  	return f
    66  }
    67  
    68  func (f *Frame) release() {
    69  	if !f.taken {
    70  		// TODO: Track cache depth and release memory.
    71  		f.frameCache, f.back = f, f.frameCache
    72  		// Clear pointers early.
    73  		f.setDict(nil)
    74  		f.globals = nil
    75  		f.code = nil
    76  	} else if f.back != nil {
    77  		f.back.taken = true
    78  	}
    79  }
    80  
    81  // pushFrame adds f to the top of the stack, above back.
    82  func (f *Frame) pushFrame(back *Frame) {
    83  	f.back = back
    84  	if back == nil {
    85  		f.threadState = newThreadState()
    86  	} else {
    87  		f.threadState = back.threadState
    88  	}
    89  }
    90  
    91  func toFrameUnsafe(o *Object) *Frame {
    92  	return (*Frame)(o.toPointer())
    93  }
    94  
    95  // Globals returns the globals dict for this frame.
    96  func (f *Frame) Globals() *Dict {
    97  	return f.globals
    98  }
    99  
   100  // ToObject upcasts f to an Object.
   101  func (f *Frame) ToObject() *Object {
   102  	return &f.Object
   103  }
   104  
   105  // SetLineno sets the current line number for the frame.
   106  func (f *Frame) SetLineno(lineno int) {
   107  	f.lineno = lineno
   108  }
   109  
   110  // State returns the current run state for f.
   111  func (f *Frame) State() RunState {
   112  	return f.state
   113  }
   114  
   115  // PushCheckpoint appends state to the end of f's checkpoint stack.
   116  func (f *Frame) PushCheckpoint(state RunState) {
   117  	f.checkpoints = append(f.checkpoints, state)
   118  }
   119  
   120  // PopCheckpoint removes the last element of f's checkpoint stack and returns
   121  // it.
   122  func (f *Frame) PopCheckpoint() {
   123  	numCheckpoints := len(f.checkpoints)
   124  	if numCheckpoints == 0 {
   125  		f.state = -1
   126  	} else {
   127  		f.state = f.checkpoints[numCheckpoints-1]
   128  		f.checkpoints = f.checkpoints[:numCheckpoints-1]
   129  	}
   130  }
   131  
   132  // Raise creates an exception and sets the exc info indicator in a way that is
   133  // compatible with the Python raise statement. The semantics are non-trivial
   134  // and are best described here:
   135  // https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement
   136  // If typ, inst and tb are all nil then the currently active exception and
   137  // traceback according to ExcInfo will be used. Raise returns the exception to
   138  // propagate.
   139  func (f *Frame) Raise(typ *Object, inst *Object, tb *Object) *BaseException {
   140  	if typ == nil && inst == nil && tb == nil {
   141  		exc, excTraceback := f.ExcInfo()
   142  		if exc != nil {
   143  			typ = exc.ToObject()
   144  		}
   145  		if excTraceback != nil {
   146  			tb = excTraceback.ToObject()
   147  		}
   148  	}
   149  	if typ == nil {
   150  		typ = None
   151  	}
   152  	if inst == nil {
   153  		inst = None
   154  	}
   155  	if tb == nil {
   156  		tb = None
   157  	}
   158  	// Build the exception if necessary.
   159  	if typ.isInstance(TypeType) {
   160  		t := toTypeUnsafe(typ)
   161  		if !t.isSubclass(BaseExceptionType) {
   162  			return f.RaiseType(TypeErrorType, fmt.Sprintf(notBaseExceptionMsg, t.Name()))
   163  		}
   164  		if !inst.isInstance(t) {
   165  			var args Args
   166  			if inst.isInstance(TupleType) {
   167  				args = toTupleUnsafe(inst).elems
   168  			} else if inst != None {
   169  				args = []*Object{inst}
   170  			}
   171  			var raised *BaseException
   172  			if inst, raised = typ.Call(f, args, nil); raised != nil {
   173  				return raised
   174  			}
   175  		}
   176  	} else if inst == None {
   177  		inst = typ
   178  	} else {
   179  		return f.RaiseType(TypeErrorType, "instance exception may not have a separate value")
   180  	}
   181  	// Validate the exception and traceback object and raise them.
   182  	if !inst.isInstance(BaseExceptionType) {
   183  		return f.RaiseType(TypeErrorType, fmt.Sprintf(notBaseExceptionMsg, inst.typ.Name()))
   184  	}
   185  	e := toBaseExceptionUnsafe(inst)
   186  	var traceback *Traceback
   187  	if tb == None {
   188  		traceback = newTraceback(f, nil)
   189  	} else if tb.isInstance(TracebackType) {
   190  		traceback = toTracebackUnsafe(tb)
   191  	} else {
   192  		return f.RaiseType(TypeErrorType, "raise: arg 3 must be a traceback or None")
   193  	}
   194  	f.RestoreExc(e, traceback)
   195  	return e
   196  }
   197  
   198  // RaiseType constructs a new object of type t, passing a single str argument
   199  // built from msg and throws the constructed object.
   200  func (f *Frame) RaiseType(t *Type, msg string) *BaseException {
   201  	return f.Raise(t.ToObject(), NewStr(msg).ToObject(), nil)
   202  }
   203  
   204  // ExcInfo returns the exception currently being handled by f's thread and the
   205  // associated traceback.
   206  func (f *Frame) ExcInfo() (*BaseException, *Traceback) {
   207  	return f.threadState.excValue, f.threadState.excTraceback
   208  }
   209  
   210  // RestoreExc assigns the exception currently being handled by f's thread and
   211  // the associated traceback. The previously set values are returned.
   212  func (f *Frame) RestoreExc(e *BaseException, tb *Traceback) (*BaseException, *Traceback) {
   213  	f.threadState.excValue, e = e, f.threadState.excValue
   214  	f.threadState.excTraceback, tb = tb, f.threadState.excTraceback
   215  	return e, tb
   216  }
   217  
   218  func (f *Frame) reprEnter(o *Object) bool {
   219  	if f.threadState.reprState[o] {
   220  		return true
   221  	}
   222  	if f.threadState.reprState == nil {
   223  		f.threadState.reprState = map[*Object]bool{}
   224  	}
   225  	f.threadState.reprState[o] = true
   226  	return false
   227  }
   228  
   229  func (f *Frame) reprLeave(o *Object) {
   230  	delete(f.threadState.reprState, o)
   231  }
   232  
   233  // MakeArgs returns an Args slice with the given length. The slice may have
   234  // been previously used, but all elements will be set to nil.
   235  func (f *Frame) MakeArgs(n int) Args {
   236  	if n == 0 {
   237  		return nil
   238  	}
   239  	if n > argsCacheArgc {
   240  		return make(Args, n)
   241  	}
   242  	numEntries := len(f.threadState.argsCache)
   243  	if numEntries == 0 {
   244  		return make(Args, n, argsCacheArgc)
   245  	}
   246  	args := f.threadState.argsCache[numEntries-1]
   247  	f.threadState.argsCache = f.threadState.argsCache[:numEntries-1]
   248  	return args[:n]
   249  }
   250  
   251  // FreeArgs clears the elements of args and returns it to the system. It may
   252  // later be returned by calls to MakeArgs and therefore references to slices of
   253  // args should not be held.
   254  func (f *Frame) FreeArgs(args Args) {
   255  	if cap(args) < argsCacheArgc {
   256  		return
   257  	}
   258  	numEntries := len(f.threadState.argsCache)
   259  	if numEntries >= argsCacheSize {
   260  		return
   261  	}
   262  	// Clear args so we don't unnecessarily hold references.
   263  	for i := len(args) - 1; i >= 0; i-- {
   264  		args[i] = nil
   265  	}
   266  	f.threadState.argsCache = f.threadState.argsCache[:numEntries+1]
   267  	f.threadState.argsCache[numEntries] = args
   268  }
   269  
   270  // FrameType is the object representing the Python 'frame' type.
   271  var FrameType = newBasisType("frame", reflect.TypeOf(Frame{}), toFrameUnsafe, ObjectType)
   272  
   273  func frameExcClear(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   274  	if raised := checkMethodArgs(f, "__exc_clear__", args, FrameType); raised != nil {
   275  		return nil, raised
   276  	}
   277  	toFrameUnsafe(args[0]).RestoreExc(nil, nil)
   278  	return None, nil
   279  }
   280  
   281  func frameExcInfo(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
   282  	if raised := checkMethodVarArgs(f, "__exc_info__", args, FrameType); raised != nil {
   283  		return nil, raised
   284  	}
   285  	excObj, tbObj := None, None
   286  	e, tb := toFrameUnsafe(args[0]).ExcInfo()
   287  	if e != nil {
   288  		excObj = e.ToObject()
   289  	}
   290  	if tb != nil {
   291  		tbObj = tb.ToObject()
   292  	}
   293  	return NewTuple2(excObj, tbObj).ToObject(), nil
   294  }
   295  
   296  func initFrameType(dict map[string]*Object) {
   297  	FrameType.flags &= ^(typeFlagInstantiable | typeFlagBasetype)
   298  	dict["__exc_clear__"] = newBuiltinFunction("__exc_clear__", frameExcClear).ToObject()
   299  	dict["__exc_info__"] = newBuiltinFunction("__exc_info__", frameExcInfo).ToObject()
   300  }