github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/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 }