github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/method.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 // Method represents Python 'instancemethod' objects. 23 type Method struct { 24 Object 25 function *Object `attr:"im_func"` 26 self *Object `attr:"im_self"` 27 class *Object `attr:"im_class"` 28 name string `attr:"__name__"` 29 } 30 31 func toMethodUnsafe(o *Object) *Method { 32 return (*Method)(o.toPointer()) 33 } 34 35 // ToObject upcasts m to an Object. 36 func (m *Method) ToObject() *Object { 37 return &m.Object 38 } 39 40 // MethodType is the object representing the Python 'instancemethod' type. 41 var MethodType = newBasisType("instancemethod", reflect.TypeOf(Method{}), toMethodUnsafe, ObjectType) 42 43 func methodCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) { 44 m := toMethodUnsafe(callable) 45 argc := len(args) 46 if m.self != nil { 47 methodArgs := f.MakeArgs(argc + 1) 48 methodArgs[0] = m.self 49 copy(methodArgs[1:], args) 50 result, raised := m.function.Call(f, methodArgs, kwargs) 51 f.FreeArgs(methodArgs) 52 return result, raised 53 } 54 if argc < 1 { 55 className, raised := methodGetMemberName(f, m.class) 56 if raised != nil { 57 return nil, raised 58 } 59 format := "unbound method %s() must be called with %s " + 60 "instance as first argument (got nothing instead)" 61 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, m.name, className)) 62 } 63 // instancemethod.__new__ ensures that m.self and m.class are not both 64 // nil. Since m.self is nil, we know that m.class is not. 65 isInst, raised := IsInstance(f, args[0], m.class) 66 if raised != nil { 67 return nil, raised 68 } 69 if !isInst { 70 className, raised := methodGetMemberName(f, m.class) 71 if raised != nil { 72 return nil, raised 73 } 74 format := "unbound method %s() must be called with %s " + 75 "instance as first argument (got %s instance instead)" 76 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, m.name, className, args[0].typ.Name())) 77 } 78 return m.function.Call(f, args, kwargs) 79 } 80 81 func methodGet(f *Frame, desc, instance *Object, owner *Type) (*Object, *BaseException) { 82 m := toMethodUnsafe(desc) 83 if m.self != nil { 84 // Don't bind a method that's already bound. 85 return desc, nil 86 } 87 if m.class != nil { 88 subcls, raised := IsSubclass(f, owner.ToObject(), m.class) 89 if raised != nil { 90 return nil, raised 91 } 92 if !subcls { 93 // Don't bind if owner is not a subclass of m.class. 94 return desc, nil 95 } 96 } 97 return (&Method{Object{typ: MethodType}, m.function, instance, owner.ToObject(), m.name}).ToObject(), nil 98 } 99 100 func methodNew(f *Frame, t *Type, args Args, _ KWArgs) (*Object, *BaseException) { 101 expectedTypes := []*Type{ObjectType, ObjectType, ObjectType} 102 argc := len(args) 103 if argc == 2 { 104 expectedTypes = expectedTypes[:2] 105 } 106 if raised := checkFunctionArgs(f, "__new__", args, expectedTypes...); raised != nil { 107 return nil, raised 108 } 109 function, self := args[0], args[1] 110 if self == None { 111 self = nil 112 } 113 var class *Object 114 if argc > 2 { 115 class = args[2] 116 } else if self == nil { 117 return nil, f.RaiseType(TypeErrorType, "unbound methods must have non-NULL im_class") 118 } 119 if function.Type().slots.Call == nil { 120 return nil, f.RaiseType(TypeErrorType, "first argument must be callable") 121 } 122 functionName, raised := methodGetMemberName(f, function) 123 if raised != nil { 124 return nil, raised 125 } 126 method := &Method{Object{typ: MethodType}, function, self, class, functionName} 127 return method.ToObject(), nil 128 } 129 130 func methodRepr(f *Frame, o *Object) (*Object, *BaseException) { 131 m := toMethodUnsafe(o) 132 s := "" 133 className, raised := methodGetMemberName(f, m.class) 134 if raised != nil { 135 return nil, raised 136 } 137 functionName, raised := methodGetMemberName(f, m.function) 138 if raised != nil { 139 return nil, raised 140 } 141 if m.self == nil { 142 s = fmt.Sprintf("<unbound method %s.%s>", className, functionName) 143 } else { 144 repr, raised := Repr(f, m.self) 145 if raised != nil { 146 return nil, raised 147 } 148 s = fmt.Sprintf("<bound method %s.%s of %s>", className, functionName, repr.Value()) 149 } 150 return NewStr(s).ToObject(), nil 151 } 152 153 func initMethodType(map[string]*Object) { 154 MethodType.flags &= ^typeFlagBasetype 155 MethodType.slots.Call = &callSlot{methodCall} 156 MethodType.slots.Get = &getSlot{methodGet} 157 MethodType.slots.Repr = &unaryOpSlot{methodRepr} 158 MethodType.slots.New = &newSlot{methodNew} 159 } 160 161 func methodGetMemberName(f *Frame, o *Object) (string, *BaseException) { 162 if o == nil { 163 return "?", nil 164 } 165 name, raised := GetAttr(f, o, internedName, None) 166 if raised != nil { 167 return "", raised 168 } 169 if !name.isInstance(BaseStringType) { 170 return "?", nil 171 } 172 nameStr, raised := ToStr(f, name) 173 if raised != nil { 174 return "", raised 175 } 176 return nameStr.Value(), nil 177 }