github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/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 grumpy 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 return &Function{Object{typ: FunctionType, dict: NewDict()}, nil, c.name, c, globals} 93 } 94 95 // newBuiltinFunction returns a function object with the given name that 96 // invokes fn when called. 97 func newBuiltinFunction(name string, fn Func) *Function { 98 return &Function{Object: Object{typ: FunctionType, dict: NewDict()}, fn: fn, name: name} 99 } 100 101 func toFunctionUnsafe(o *Object) *Function { 102 return (*Function)(o.toPointer()) 103 } 104 105 // ToObject upcasts f to an Object. 106 func (f *Function) ToObject() *Object { 107 return &f.Object 108 } 109 110 // Name returns f's name field. 111 func (f *Function) Name() string { 112 return f.name 113 } 114 115 func functionCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) { 116 fun := toFunctionUnsafe(callable) 117 code := fun.code 118 if code == nil { 119 return fun.fn(f, args, kwargs) 120 } 121 return code.Eval(f, fun.globals, args, kwargs) 122 } 123 124 func functionGet(f *Frame, desc, instance *Object, owner *Type) (*Object, *BaseException) { 125 args := f.MakeArgs(3) 126 args[0] = desc 127 args[1] = instance 128 args[2] = owner.ToObject() 129 ret, raised := MethodType.Call(f, args, nil) 130 f.FreeArgs(args) 131 return ret, raised 132 } 133 134 func functionRepr(_ *Frame, o *Object) (*Object, *BaseException) { 135 fun := toFunctionUnsafe(o) 136 return NewStr(fmt.Sprintf("<%s %s at %p>", fun.typ.Name(), fun.Name(), fun)).ToObject(), nil 137 } 138 139 func initFunctionType(map[string]*Object) { 140 FunctionType.flags &= ^(typeFlagInstantiable | typeFlagBasetype) 141 FunctionType.slots.Call = &callSlot{functionCall} 142 FunctionType.slots.Get = &getSlot{functionGet} 143 FunctionType.slots.Repr = &unaryOpSlot{functionRepr} 144 } 145 146 // staticMethod represents Python 'staticmethod' objects. 147 type staticMethod struct { 148 Object 149 callable *Object 150 } 151 152 func newStaticMethod(callable *Object) *staticMethod { 153 return &staticMethod{Object{typ: StaticMethodType}, callable} 154 } 155 156 func toStaticMethodUnsafe(o *Object) *staticMethod { 157 return (*staticMethod)(o.toPointer()) 158 } 159 160 // ToObject upcasts f to an Object. 161 func (m *staticMethod) ToObject() *Object { 162 return &m.Object 163 } 164 165 func staticMethodGet(f *Frame, desc, _ *Object, _ *Type) (*Object, *BaseException) { 166 m := toStaticMethodUnsafe(desc) 167 if m.callable == nil { 168 return nil, f.RaiseType(RuntimeErrorType, "uninitialized staticmethod object") 169 } 170 return m.callable, nil 171 } 172 173 func staticMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { 174 if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil { 175 return nil, raised 176 } 177 toStaticMethodUnsafe(o).callable = args[0] 178 return None, nil 179 } 180 181 func initStaticMethodType(map[string]*Object) { 182 StaticMethodType.slots.Get = &getSlot{staticMethodGet} 183 StaticMethodType.slots.Init = &initSlot{staticMethodInit} 184 } 185 186 // classMethod represents Python 'classmethod' objects. 187 type classMethod struct { 188 Object 189 callable *Object 190 } 191 192 func newClassMethod(callable *Object) *classMethod { 193 return &classMethod{Object{typ: ClassMethodType}, callable} 194 } 195 196 func toClassMethodUnsafe(o *Object) *classMethod { 197 return (*classMethod)(o.toPointer()) 198 } 199 200 // ToObject upcasts f to an Object. 201 func (m *classMethod) ToObject() *Object { 202 return &m.Object 203 } 204 205 func classMethodGet(f *Frame, desc, _ *Object, owner *Type) (*Object, *BaseException) { 206 m := toClassMethodUnsafe(desc) 207 if m.callable == nil { 208 return nil, f.RaiseType(RuntimeErrorType, "uninitialized classmethod object") 209 } 210 args := f.MakeArgs(3) 211 args[0] = m.callable 212 args[1] = owner.ToObject() 213 args[2] = args[1] 214 ret, raised := MethodType.Call(f, args, nil) 215 f.FreeArgs(args) 216 return ret, raised 217 } 218 219 func classMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { 220 if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil { 221 return nil, raised 222 } 223 toClassMethodUnsafe(o).callable = args[0] 224 return None, nil 225 } 226 227 func initClassMethodType(map[string]*Object) { 228 ClassMethodType.slots.Get = &getSlot{classMethodGet} 229 ClassMethodType.slots.Init = &initSlot{classMethodInit} 230 }