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