github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/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 runtime 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 127 d := newStringDict(map[string]*Object{ 128 "__doc__": None, 129 }) 130 method := &Method{Object{typ: MethodType, dict: d}, function, self, class, functionName} 131 return method.ToObject(), nil 132 } 133 134 func methodRepr(f *Frame, o *Object) (*Object, *BaseException) { 135 m := toMethodUnsafe(o) 136 s := "" 137 className, raised := methodGetMemberName(f, m.class) 138 if raised != nil { 139 return nil, raised 140 } 141 functionName, raised := methodGetMemberName(f, m.function) 142 if raised != nil { 143 return nil, raised 144 } 145 if m.self == nil { 146 s = fmt.Sprintf("<unbound method %s.%s>", className, functionName) 147 } else { 148 repr, raised := Repr(f, m.self) 149 if raised != nil { 150 return nil, raised 151 } 152 s = fmt.Sprintf("<bound method %s.%s of %s>", className, functionName, repr.Value()) 153 } 154 return NewStr(s).ToObject(), nil 155 } 156 157 func initMethodType(map[string]*Object) { 158 MethodType.flags &= ^typeFlagBasetype 159 MethodType.slots.Call = &callSlot{methodCall} 160 MethodType.slots.Get = &getSlot{methodGet} 161 MethodType.slots.Repr = &unaryOpSlot{methodRepr} 162 MethodType.slots.New = &newSlot{methodNew} 163 } 164 165 func methodGetMemberName(f *Frame, o *Object) (string, *BaseException) { 166 if o == nil { 167 return "?", nil 168 } 169 name, raised := GetAttr(f, o, internedName, None) 170 if raised != nil { 171 return "", raised 172 } 173 if !name.isInstance(BaseStringType) { 174 return "?", nil 175 } 176 nameStr, raised := ToStr(f, name) 177 if raised != nil { 178 return "", raised 179 } 180 return nameStr.Value(), nil 181 }