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  }