github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/object.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  	"sync/atomic"
    21  	"unsafe"
    22  )
    23  
    24  var (
    25  	objectBasis             = reflect.TypeOf(Object{})
    26  	objectReconstructorFunc = newBuiltinFunction("_reconstructor", objectReconstructor).ToObject()
    27  	objectReduceFunc        = newBuiltinFunction("__reduce__", objectReduce).ToObject()
    28  	// ObjectType is the object representing the Python 'object' type.
    29  	//
    30  	// We don't use newBasisType() here since that introduces an initialization
    31  	// cycle between TypeType and ObjectType.
    32  	ObjectType = &Type{
    33  		name:  "object",
    34  		basis: objectBasis,
    35  		flags: typeFlagDefault,
    36  		slots: typeSlots{Basis: &basisSlot{objectBasisFunc}},
    37  	}
    38  )
    39  
    40  // Object represents Python 'object' objects.
    41  type Object struct {
    42  	typ  *Type `attr:"__class__"`
    43  	dict *Dict
    44  	ref  *WeakRef
    45  }
    46  
    47  func newObject(t *Type) *Object {
    48  	var dict *Dict
    49  	if t != ObjectType {
    50  		dict = NewDict()
    51  	}
    52  	o := (*Object)(unsafe.Pointer(reflect.New(t.basis).Pointer()))
    53  	o.typ = t
    54  	o.setDict(dict)
    55  	return o
    56  }
    57  
    58  // Call invokes the callable Python object o with the given positional and
    59  // keyword args. args must be non-nil (but can be empty). kwargs can be nil.
    60  func (o *Object) Call(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) {
    61  	call := o.Type().slots.Call
    62  	if call == nil {
    63  		return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("'%s' object is not callable", o.Type().Name()))
    64  	}
    65  	return call.Fn(f, o, args, kwargs)
    66  }
    67  
    68  // Dict returns o's object dict, aka __dict__.
    69  func (o *Object) Dict() *Dict {
    70  	p := (*unsafe.Pointer)(unsafe.Pointer(&o.dict))
    71  	return (*Dict)(atomic.LoadPointer(p))
    72  }
    73  
    74  func (o *Object) setDict(d *Dict) {
    75  	p := (*unsafe.Pointer)(unsafe.Pointer(&o.dict))
    76  	atomic.StorePointer(p, unsafe.Pointer(d))
    77  }
    78  
    79  // String returns a string representation of o, e.g. for debugging.
    80  func (o *Object) String() string {
    81  	if o == nil {
    82  		return "nil"
    83  	}
    84  	s, raised := Repr(NewRootFrame(), o)
    85  	if raised != nil {
    86  		return fmt.Sprintf("<%s object (repr raised %s)>", o.typ.Name(), raised.typ.Name())
    87  	}
    88  	return s.Value()
    89  }
    90  
    91  // Type returns the Python type of o.
    92  func (o *Object) Type() *Type {
    93  	return o.typ
    94  }
    95  
    96  func (o *Object) toPointer() unsafe.Pointer {
    97  	return unsafe.Pointer(o)
    98  }
    99  
   100  func (o *Object) isInstance(t *Type) bool {
   101  	return o.typ.isSubclass(t)
   102  }
   103  
   104  func objectBasisFunc(o *Object) reflect.Value {
   105  	return reflect.ValueOf(o).Elem()
   106  }
   107  
   108  func objectDelAttr(f *Frame, o *Object, name *Str) *BaseException {
   109  	desc, raised := o.typ.mroLookup(f, name)
   110  	if raised != nil {
   111  		return raised
   112  	}
   113  	if desc != nil {
   114  		if del := desc.Type().slots.Delete; del != nil {
   115  			return del.Fn(f, desc, o)
   116  		}
   117  	}
   118  	deleted := false
   119  	d := o.Dict()
   120  	if d != nil {
   121  		deleted, raised = d.DelItem(f, name.ToObject())
   122  		if raised != nil {
   123  			return raised
   124  		}
   125  	}
   126  	if !deleted {
   127  		format := "'%s' object has no attribute '%s'"
   128  		return f.RaiseType(AttributeErrorType, fmt.Sprintf(format, o.typ.Name(), name.Value()))
   129  	}
   130  	return nil
   131  }
   132  
   133  // objectGetAttribute implements the spec here:
   134  // https://docs.python.org/2/reference/datamodel.html#invoking-descriptors
   135  func objectGetAttribute(f *Frame, o *Object, name *Str) (*Object, *BaseException) {
   136  	// Look for a data descriptor in the type.
   137  	var typeGet *getSlot
   138  	typeAttr, raised := o.typ.mroLookup(f, name)
   139  	if raised != nil {
   140  		return nil, raised
   141  	}
   142  	if typeAttr != nil {
   143  		typeGet = typeAttr.typ.slots.Get
   144  		if typeGet != nil && (typeAttr.typ.slots.Set != nil || typeAttr.typ.slots.Delete != nil) {
   145  			return typeGet.Fn(f, typeAttr, o, o.Type())
   146  		}
   147  	}
   148  	// Look in the object's dict.
   149  	if d := o.Dict(); d != nil {
   150  		value, raised := d.GetItem(f, name.ToObject())
   151  		if value != nil || raised != nil {
   152  			return value, raised
   153  		}
   154  	}
   155  	// Use the (non-data) descriptor from the type.
   156  	if typeGet != nil {
   157  		return typeGet.Fn(f, typeAttr, o, o.Type())
   158  	}
   159  	// Return the ordinary type attribute.
   160  	if typeAttr != nil {
   161  		return typeAttr, nil
   162  	}
   163  	format := "'%s' object has no attribute '%s'"
   164  	return nil, f.RaiseType(AttributeErrorType, fmt.Sprintf(format, o.typ.Name(), name.Value()))
   165  }
   166  
   167  func objectHash(f *Frame, o *Object) (*Object, *BaseException) {
   168  	return NewInt(int(uintptr(o.toPointer()))).ToObject(), nil
   169  }
   170  
   171  func objectNew(f *Frame, t *Type, _ Args, _ KWArgs) (*Object, *BaseException) {
   172  	if t.flags&typeFlagInstantiable == 0 {
   173  		format := "object.__new__(%s) is not safe, use %s.__new__()"
   174  		return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, t.Name(), t.Name()))
   175  	}
   176  	return newObject(t), nil
   177  }
   178  
   179  func objectReduce(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   180  	expectedTypes := []*Type{ObjectType, IntType}
   181  	argc := len(args)
   182  	if argc == 1 {
   183  		expectedTypes = expectedTypes[:1]
   184  	}
   185  	if raised := checkMethodArgs(f, "__reduce__", args, expectedTypes...); raised != nil {
   186  		return nil, raised
   187  	}
   188  	return objectReduceCommon(f, args)
   189  }
   190  
   191  func objectReduceEx(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   192  	expectedTypes := []*Type{ObjectType, IntType}
   193  	argc := len(args)
   194  	if argc == 1 {
   195  		expectedTypes = expectedTypes[:1]
   196  	}
   197  	if raised := checkMethodArgs(f, "__reduce_ex__", args, expectedTypes...); raised != nil {
   198  		return nil, raised
   199  	}
   200  	reduce, raised := args[0].typ.mroLookup(f, NewStr("__reduce__"))
   201  	if raised != nil {
   202  		return nil, raised
   203  	}
   204  	if reduce != nil && reduce != objectReduceFunc {
   205  		// __reduce__ is overridden so prefer using it.
   206  		return reduce.Call(f, args, nil)
   207  	}
   208  	return objectReduceCommon(f, args)
   209  }
   210  
   211  func objectSetAttr(f *Frame, o *Object, name *Str, value *Object) *BaseException {
   212  	if typeAttr, raised := o.typ.mroLookup(f, name); raised != nil {
   213  		return raised
   214  	} else if typeAttr != nil {
   215  		if typeSet := typeAttr.typ.slots.Set; typeSet != nil {
   216  			return typeSet.Fn(f, typeAttr, o, value)
   217  		}
   218  	}
   219  	if d := o.Dict(); d != nil {
   220  		if raised := d.SetItem(f, name.ToObject(), value); raised == nil || !raised.isInstance(KeyErrorType) {
   221  			return nil
   222  		}
   223  	}
   224  	return f.RaiseType(AttributeErrorType, fmt.Sprintf("'%s' has no attribute '%s'", o.typ.Name(), name.Value()))
   225  }
   226  
   227  func initObjectType(dict map[string]*Object) {
   228  	ObjectType.typ = TypeType
   229  	dict["__reduce__"] = objectReduceFunc
   230  	dict["__reduce_ex__"] = newBuiltinFunction("__reduce_ex__", objectReduceEx).ToObject()
   231  	dict["__dict__"] = newProperty(newBuiltinFunction("_get_dict", objectGetDict).ToObject(), newBuiltinFunction("_set_dict", objectSetDict).ToObject(), nil).ToObject()
   232  	ObjectType.slots.DelAttr = &delAttrSlot{objectDelAttr}
   233  	ObjectType.slots.GetAttribute = &getAttributeSlot{objectGetAttribute}
   234  	ObjectType.slots.Hash = &unaryOpSlot{objectHash}
   235  	ObjectType.slots.New = &newSlot{objectNew}
   236  	ObjectType.slots.SetAttr = &setAttrSlot{objectSetAttr}
   237  }
   238  
   239  // objectReconstructor builds an object from a class, its basis type and its
   240  // state (e.g. its string or integer value). It is similar to the
   241  // copy_reg._reconstructor function in CPython.
   242  func objectReconstructor(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   243  	if raised := checkFunctionArgs(f, "_reconstructor", args, TypeType, TypeType, ObjectType); raised != nil {
   244  		return nil, raised
   245  	}
   246  	t, basisType, state := toTypeUnsafe(args[0]), toTypeUnsafe(args[1]), args[2]
   247  	newMethod, raised := GetAttr(f, basisType.ToObject(), NewStr("__new__"), nil)
   248  	if raised != nil {
   249  		return nil, raised
   250  	}
   251  	o, raised := newMethod.Call(f, Args{t.ToObject(), state}, nil)
   252  	if raised != nil {
   253  		return nil, raised
   254  	}
   255  	if basisType != ObjectType {
   256  		initMethod, raised := GetAttr(f, basisType.ToObject(), NewStr("__init__"), None)
   257  		if raised != nil {
   258  			return nil, raised
   259  		}
   260  		if initMethod != None {
   261  			if _, raised := initMethod.Call(f, Args{o, state}, nil); raised != nil {
   262  				return nil, raised
   263  			}
   264  		}
   265  	}
   266  	return o, nil
   267  }
   268  
   269  func objectReduceCommon(f *Frame, args Args) (*Object, *BaseException) {
   270  	// TODO: Support __getstate__ and __getnewargs__.
   271  	o := args[0]
   272  	t := o.Type()
   273  	proto := 0
   274  	if len(args) > 1 {
   275  		proto = toIntUnsafe(args[1]).Value()
   276  	}
   277  	var raised *BaseException
   278  	if proto < 2 {
   279  		basisType := basisTypes[t.basis]
   280  		if basisType == t {
   281  			// Basis types are handled elsewhere by the pickle and
   282  			// copy frameworks. This matches behavior in
   283  			// copy_reg._reduce_ex in CPython.
   284  			return nil, f.RaiseType(TypeErrorType, fmt.Sprintf("can't pickle %s objects", t.Name()))
   285  		}
   286  		state := None
   287  		if basisType != ObjectType {
   288  			// For subclasses of basis types having state (e.g.
   289  			// integer values), the state is captured by creating
   290  			// an instance of that basis type.
   291  			if state, raised = basisType.Call(f, Args{o}, nil); raised != nil {
   292  				return nil, raised
   293  			}
   294  		}
   295  		newArgs := NewTuple3(t.ToObject(), basisType.ToObject(), state).ToObject()
   296  		if d := o.Dict(); d != nil {
   297  			return NewTuple3(objectReconstructorFunc, newArgs, d.ToObject()).ToObject(), nil
   298  		}
   299  		return NewTuple2(objectReconstructorFunc, newArgs).ToObject(), nil
   300  	}
   301  	newArgs := []*Object{t.ToObject()}
   302  	getNewArgsMethod, raised := GetAttr(f, o, NewStr("__getnewargs__"), None)
   303  	if raised != nil {
   304  		return nil, raised
   305  	}
   306  	if getNewArgsMethod != None {
   307  		extraNewArgs, raised := getNewArgsMethod.Call(f, nil, nil)
   308  		if raised != nil {
   309  			return nil, raised
   310  		}
   311  		if !extraNewArgs.isInstance(TupleType) {
   312  			format := "__getnewargs__ should return a tuple, not '%s'"
   313  			return nil, f.RaiseType(TypeErrorType, fmt.Sprintf(format, extraNewArgs.Type().Name()))
   314  		}
   315  		newArgs = append(newArgs, toTupleUnsafe(extraNewArgs).elems...)
   316  	}
   317  	dict := None
   318  	if d := o.Dict(); d != nil {
   319  		dict = d.ToObject()
   320  	}
   321  	// For proto >= 2 include list and dict items.
   322  	listItems := None
   323  	if o.isInstance(ListType) {
   324  		if listItems, raised = Iter(f, o); raised != nil {
   325  			return nil, raised
   326  		}
   327  	}
   328  	dictItems := None
   329  	if o.isInstance(DictType) {
   330  		iterItems, raised := o.typ.mroLookup(f, NewStr("iteritems"))
   331  		if raised != nil {
   332  			return nil, raised
   333  		}
   334  		if iterItems != nil {
   335  			if dictItems, raised = iterItems.Call(f, Args{o}, nil); raised != nil {
   336  				return nil, raised
   337  			}
   338  		}
   339  	}
   340  	newFunc, raised := GetAttr(f, t.ToObject(), NewStr("__new__"), nil)
   341  	if raised != nil {
   342  		return nil, raised
   343  	}
   344  	return NewTuple5(newFunc, NewTuple(newArgs...).ToObject(), dict, listItems, dictItems).ToObject(), nil
   345  }
   346  
   347  func objectGetDict(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   348  	if raised := checkMethodArgs(f, "_get_dict", args, ObjectType); raised != nil {
   349  		return nil, raised
   350  	}
   351  	o := args[0]
   352  	d := o.Dict()
   353  	if d == nil {
   354  		format := "'%s' object has no attribute '__dict__'"
   355  		return nil, f.RaiseType(AttributeErrorType, fmt.Sprintf(format, o.typ.Name()))
   356  	}
   357  	return args[0].Dict().ToObject(), nil
   358  }
   359  
   360  func objectSetDict(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) {
   361  	if raised := checkMethodArgs(f, "_set_dict", args, ObjectType, DictType); raised != nil {
   362  		return nil, raised
   363  	}
   364  	o := args[0]
   365  	if o.Type() == ObjectType {
   366  		format := "'%s' object has no attribute '__dict__'"
   367  		return nil, f.RaiseType(AttributeErrorType, fmt.Sprintf(format, o.typ.Name()))
   368  	}
   369  	o.setDict(toDictUnsafe(args[1]))
   370  	return None, nil
   371  }