github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/function.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 runtime 16 17 import ( 18 "fmt" 19 "reflect" 20 ) 21 22 var ( 23 // FunctionType is the object representing the Python 'function' type. 24 FunctionType = newBasisType("function", reflect.TypeOf(Function{}), toFunctionUnsafe, ObjectType) 25 // StaticMethodType is the object representing the Python 26 // 'staticmethod' type. 27 StaticMethodType = newBasisType("staticmethod", reflect.TypeOf(staticMethod{}), toStaticMethodUnsafe, ObjectType) 28 // ClassMethodType is the object representing the Python 29 // 'classmethod' type. 30 ClassMethodType = newBasisType("classmethod", reflect.TypeOf(classMethod{}), toClassMethodUnsafe, ObjectType) 31 ) 32 33 // Args represent positional parameters in a call to a Python function. 34 type Args []*Object 35 36 func (a Args) makeCopy() Args { 37 result := make(Args, len(a)) 38 copy(result, a) 39 return result 40 } 41 42 // KWArg represents a keyword argument in a call to a Python function. 43 type KWArg struct { 44 Name string 45 Value *Object 46 } 47 48 // KWArgs represents a list of keyword parameters in a call to a Python 49 // function. 50 type KWArgs []KWArg 51 52 // String returns a string representation of k, e.g. for debugging. 53 func (k KWArgs) String() string { 54 return k.makeDict().String() 55 } 56 57 func (k KWArgs) get(name string, def *Object) *Object { 58 for _, kwarg := range k { 59 if kwarg.Name == name { 60 return kwarg.Value 61 } 62 } 63 return def 64 } 65 66 func (k KWArgs) makeDict() *Dict { 67 m := map[string]*Object{} 68 for _, kw := range k { 69 m[kw.Name] = kw.Value 70 } 71 return newStringDict(m) 72 } 73 74 // Func is a Go function underlying a Python Function object. 75 type Func func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) 76 77 // Function represents Python 'function' objects. 78 type Function struct { 79 Object 80 fn Func 81 name string `attr:"__name__"` 82 code *Code `attr:"func_code"` 83 globals *Dict `attr:"func_globals"` 84 } 85 86 // NewFunction creates a function object corresponding to a Python function 87 // taking the given args, vararg and kwarg. When called, the arguments are 88 // validated before calling fn. This includes checking that an appropriate 89 // number of arguments are provided, populating *args and **kwargs if 90 // necessary, etc. 91 func NewFunction(c *Code, globals *Dict) *Function { 92 d := newStringDict(map[string]*Object{ 93 "__doc__": None, 94 }) 95 return &Function{Object{typ: FunctionType, dict: d}, nil, c.name, c, globals} 96 } 97 98 // newBuiltinFunction returns a function object with the given name that 99 // invokes fn when called. 100 func newBuiltinFunction(name string, fn Func) *Function { 101 return &Function{Object: Object{typ: FunctionType, dict: NewDict()}, fn: fn, name: name} 102 } 103 104 func toFunctionUnsafe(o *Object) *Function { 105 return (*Function)(o.toPointer()) 106 } 107 108 // ToObject upcasts f to an Object. 109 func (f *Function) ToObject() *Object { 110 return &f.Object 111 } 112 113 // Name returns f's name field. 114 func (f *Function) Name() string { 115 return f.name 116 } 117 118 func functionCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) { 119 fun := toFunctionUnsafe(callable) 120 code := fun.code 121 if code == nil { 122 return fun.fn(f, args, kwargs) 123 } 124 return code.Eval(f, fun.globals, args, kwargs) 125 } 126 127 func functionGet(f *Frame, desc, instance *Object, owner *Type) (*Object, *BaseException) { 128 args := f.MakeArgs(3) 129 args[0] = desc 130 args[1] = instance 131 args[2] = owner.ToObject() 132 ret, raised := MethodType.Call(f, args, nil) 133 f.FreeArgs(args) 134 return ret, raised 135 } 136 137 func functionRepr(_ *Frame, o *Object) (*Object, *BaseException) { 138 fun := toFunctionUnsafe(o) 139 return NewStr(fmt.Sprintf("<%s %s at %p>", fun.typ.Name(), fun.Name(), fun)).ToObject(), nil 140 } 141 142 func initFunctionType(map[string]*Object) { 143 FunctionType.flags &= ^(typeFlagInstantiable | typeFlagBasetype) 144 FunctionType.slots.Call = &callSlot{functionCall} 145 FunctionType.slots.Get = &getSlot{functionGet} 146 FunctionType.slots.Repr = &unaryOpSlot{functionRepr} 147 } 148 149 // staticMethod represents Python 'staticmethod' objects. 150 type staticMethod struct { 151 Object 152 callable *Object 153 } 154 155 func newStaticMethod(callable *Object) *staticMethod { 156 return &staticMethod{Object{typ: StaticMethodType}, callable} 157 } 158 159 func toStaticMethodUnsafe(o *Object) *staticMethod { 160 return (*staticMethod)(o.toPointer()) 161 } 162 163 // ToObject upcasts f to an Object. 164 func (m *staticMethod) ToObject() *Object { 165 return &m.Object 166 } 167 168 func staticMethodGet(f *Frame, desc, _ *Object, _ *Type) (*Object, *BaseException) { 169 m := toStaticMethodUnsafe(desc) 170 if m.callable == nil { 171 return nil, f.RaiseType(RuntimeErrorType, "uninitialized staticmethod object") 172 } 173 return m.callable, nil 174 } 175 176 func staticMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { 177 if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil { 178 return nil, raised 179 } 180 toStaticMethodUnsafe(o).callable = args[0] 181 return None, nil 182 } 183 184 func initStaticMethodType(map[string]*Object) { 185 StaticMethodType.slots.Get = &getSlot{staticMethodGet} 186 StaticMethodType.slots.Init = &initSlot{staticMethodInit} 187 } 188 189 // classMethod represents Python 'classmethod' objects. 190 type classMethod struct { 191 Object 192 callable *Object 193 } 194 195 func newClassMethod(callable *Object) *classMethod { 196 return &classMethod{Object{typ: ClassMethodType}, callable} 197 } 198 199 func toClassMethodUnsafe(o *Object) *classMethod { 200 return (*classMethod)(o.toPointer()) 201 } 202 203 // ToObject upcasts f to an Object. 204 func (m *classMethod) ToObject() *Object { 205 return &m.Object 206 } 207 208 func classMethodGet(f *Frame, desc, _ *Object, owner *Type) (*Object, *BaseException) { 209 m := toClassMethodUnsafe(desc) 210 if m.callable == nil { 211 return nil, f.RaiseType(RuntimeErrorType, "uninitialized classmethod object") 212 } 213 args := f.MakeArgs(3) 214 args[0] = m.callable 215 args[1] = owner.ToObject() 216 args[2] = args[1] 217 ret, raised := MethodType.Call(f, args, nil) 218 f.FreeArgs(args) 219 return ret, raised 220 } 221 222 func classMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { 223 if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil { 224 return nil, raised 225 } 226 toClassMethodUnsafe(o).callable = args[0] 227 return None, nil 228 } 229 230 func initClassMethodType(map[string]*Object) { 231 ClassMethodType.slots.Get = &getSlot{classMethodGet} 232 ClassMethodType.slots.Init = &initSlot{classMethodInit} 233 }