github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/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  }