github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/descriptor.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 type fieldDescriptorType int 23 24 const ( 25 fieldDescriptorRO fieldDescriptorType = iota 26 fieldDescriptorRW 27 ) 28 29 // Property represents Python 'property' objects. 30 type Property struct { 31 Object 32 get, set, del *Object 33 } 34 35 func newProperty(get, set, del *Object) *Property { 36 return &Property{Object{typ: PropertyType}, get, set, del} 37 } 38 39 func toPropertyUnsafe(o *Object) *Property { 40 return (*Property)(o.toPointer()) 41 } 42 43 // ToObject upcasts p to an Object. 44 func (p *Property) ToObject() *Object { 45 return &p.Object 46 } 47 48 // PropertyType is the object representing the Python 'property' type. 49 var PropertyType = newBasisType("property", reflect.TypeOf(Property{}), toPropertyUnsafe, ObjectType) 50 51 func initPropertyType(map[string]*Object) { 52 PropertyType.slots.Delete = &deleteSlot{propertyDelete} 53 PropertyType.slots.Get = &getSlot{propertyGet} 54 PropertyType.slots.Init = &initSlot{propertyInit} 55 PropertyType.slots.Set = &setSlot{propertySet} 56 } 57 58 func propertyDelete(f *Frame, desc, inst *Object) *BaseException { 59 p := toPropertyUnsafe(desc) 60 if p.del == nil || p.del == None { 61 return f.RaiseType(AttributeErrorType, "can't delete attribute") 62 } 63 _, raised := p.del.Call(f, Args{inst}, nil) 64 return raised 65 } 66 67 func propertyGet(f *Frame, desc, instance *Object, _ *Type) (*Object, *BaseException) { 68 p := toPropertyUnsafe(desc) 69 if p.get == nil || p.get == None { 70 return nil, f.RaiseType(AttributeErrorType, "unreadable attribute") 71 } 72 return p.get.Call(f, Args{instance}, nil) 73 } 74 75 func propertyInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { 76 expectedTypes := []*Type{ObjectType, ObjectType, ObjectType} 77 argc := len(args) 78 if argc < 3 { 79 expectedTypes = expectedTypes[:argc] 80 } 81 if raised := checkFunctionArgs(f, "__init__", args, expectedTypes...); raised != nil { 82 return nil, raised 83 } 84 p := toPropertyUnsafe(o) 85 if argc > 0 { 86 p.get = args[0] 87 } 88 if argc > 1 { 89 p.set = args[1] 90 } 91 if argc > 2 { 92 p.del = args[2] 93 } 94 return None, nil 95 } 96 97 func propertySet(f *Frame, desc, inst, value *Object) *BaseException { 98 p := toPropertyUnsafe(desc) 99 if p.set == nil || p.set == None { 100 return f.RaiseType(AttributeErrorType, "can't set attribute") 101 } 102 _, raised := p.set.Call(f, Args{inst, value}, nil) 103 return raised 104 } 105 106 // makeStructFieldDescriptor creates a descriptor with a getter that returns 107 // the field given by fieldName from t's basis structure. 108 func makeStructFieldDescriptor(t *Type, fieldName, propertyName string, fieldMode fieldDescriptorType) *Object { 109 field, ok := t.basis.FieldByName(fieldName) 110 if !ok { 111 logFatal(fmt.Sprintf("no such field %q for basis %s", fieldName, nativeTypeName(t.basis))) 112 } 113 114 getterFunc := func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 115 if raised := checkFunctionArgs(f, fieldName, args, ObjectType); raised != nil { 116 return nil, raised 117 } 118 119 self := args[0] 120 if !self.isInstance(t) { 121 format := "descriptor '%s' for '%s' objects doesn't apply to '%s' objects" 122 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, propertyName, t.Name(), self.typ.Name())) 123 } 124 125 return WrapNative(f, t.slots.Basis.Fn(self).FieldByIndex(field.Index)) 126 } 127 getter := newBuiltinFunction("_get"+fieldName, getterFunc).ToObject() 128 129 setter := None 130 if fieldMode == fieldDescriptorRW { 131 if field.PkgPath != "" { 132 logFatal(fmt.Sprintf("field '%q' is not public on Golang code. Please fix it.", fieldName)) 133 } 134 135 setterFunc := func(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 136 if raised := checkFunctionArgs(f, fieldName, args, ObjectType, ObjectType); raised != nil { 137 return nil, raised 138 } 139 140 self := args[0] 141 newValue := args[1] 142 143 if !self.isInstance(t) { 144 format := "descriptor '%s' for '%s' objects doesn't apply to '%s' objects" 145 return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, propertyName, t.Name(), self.typ.Name())) 146 } 147 148 val := t.slots.Basis.Fn(self).FieldByIndex(field.Index) 149 converted, raised := maybeConvertValue(f, newValue, field.Type) 150 if raised != nil { 151 return nil, raised 152 } 153 154 val.Set(converted) 155 return None, nil 156 } 157 158 setter = newBuiltinFunction("_set"+fieldName, setterFunc).ToObject() 159 } 160 return newProperty(getter, setter, None).ToObject() 161 }