github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/runtime/function.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  var (
    23  	// FunctionType is the object representing the Python 'function' type.
    24  	FunctionType = newBasisType("function", reflect.TypeOf(Function{}), toFunctionUnsafe, ObjectType)
    25  	// StaticMethodType is the object representing the Python
    26  	// 'staticmethod' type.
    27  	StaticMethodType = newBasisType("staticmethod", reflect.TypeOf(staticMethod{}), toStaticMethodUnsafe, ObjectType)
    28  	// ClassMethodType is the object representing the Python
    29  	// 'classmethod' type.
    30  	ClassMethodType = newBasisType("classmethod", reflect.TypeOf(classMethod{}), toClassMethodUnsafe, ObjectType)
    31  )
    32  
    33  // Args represent positional parameters in a call to a Python function.
    34  type Args []*Object
    35  
    36  func (a Args) makeCopy() Args {
    37  	result := make(Args, len(a))
    38  	copy(result, a)
    39  	return result
    40  }
    41  
    42  // KWArg represents a keyword argument in a call to a Python function.
    43  type KWArg struct {
    44  	Name  string
    45  	Value *Object
    46  }
    47  
    48  // KWArgs represents a list of keyword parameters in a call to a Python
    49  // function.
    50  type KWArgs []KWArg
    51  
    52  // String returns a string representation of k, e.g. for debugging.
    53  func (k KWArgs) String() string {
    54  	return k.makeDict().String()
    55  }
    56  
    57  func (k KWArgs) get(name string, def *Object) *Object {
    58  	for _, kwarg := range k {
    59  		if kwarg.Name == name {
    60  			return kwarg.Value
    61  		}
    62  	}
    63  	return def
    64  }
    65  
    66  func (k KWArgs) makeDict() *Dict {
    67  	m := map[string]*Object{}
    68  	for _, kw := range k {
    69  		m[kw.Name] = kw.Value
    70  	}
    71  	return newStringDict(m)
    72  }
    73  
    74  // Func is a Go function underlying a Python Function object.
    75  type Func func(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException)
    76  
    77  // Function represents Python 'function' objects.
    78  type Function struct {
    79  	Object
    80  	fn      Func
    81  	name    string `attr:"__name__"`
    82  	code    *Code  `attr:"func_code"`
    83  	globals *Dict  `attr:"func_globals"`
    84  }
    85  
    86  // NewFunction creates a function object corresponding to a Python function
    87  // taking the given args, vararg and kwarg. When called, the arguments are
    88  // validated before calling fn. This includes checking that an appropriate
    89  // number of arguments are provided, populating *args and **kwargs if
    90  // necessary, etc.
    91  func NewFunction(c *Code, globals *Dict) *Function {
    92  	d := newStringDict(map[string]*Object{
    93  		"__doc__": None,
    94  	})
    95  	return &Function{Object{typ: FunctionType, dict: d}, nil, c.name, c, globals}
    96  }
    97  
    98  // newBuiltinFunction returns a function object with the given name that
    99  // invokes fn when called.
   100  func newBuiltinFunction(name string, fn Func) *Function {
   101  	return &Function{Object: Object{typ: FunctionType, dict: NewDict()}, fn: fn, name: name}
   102  }
   103  
   104  func toFunctionUnsafe(o *Object) *Function {
   105  	return (*Function)(o.toPointer())
   106  }
   107  
   108  // ToObject upcasts f to an Object.
   109  func (f *Function) ToObject() *Object {
   110  	return &f.Object
   111  }
   112  
   113  // Name returns f's name field.
   114  func (f *Function) Name() string {
   115  	return f.name
   116  }
   117  
   118  func functionCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) {
   119  	fun := toFunctionUnsafe(callable)
   120  	code := fun.code
   121  	if code == nil {
   122  		return fun.fn(f, args, kwargs)
   123  	}
   124  	return code.Eval(f, fun.globals, args, kwargs)
   125  }
   126  
   127  func functionGet(f *Frame, desc, instance *Object, owner *Type) (*Object, *BaseException) {
   128  	args := f.MakeArgs(3)
   129  	args[0] = desc
   130  	args[1] = instance
   131  	args[2] = owner.ToObject()
   132  	ret, raised := MethodType.Call(f, args, nil)
   133  	f.FreeArgs(args)
   134  	return ret, raised
   135  }
   136  
   137  func functionRepr(_ *Frame, o *Object) (*Object, *BaseException) {
   138  	fun := toFunctionUnsafe(o)
   139  	return NewStr(fmt.Sprintf("<%s %s at %p>", fun.typ.Name(), fun.Name(), fun)).ToObject(), nil
   140  }
   141  
   142  func initFunctionType(map[string]*Object) {
   143  	FunctionType.flags &= ^(typeFlagInstantiable | typeFlagBasetype)
   144  	FunctionType.slots.Call = &callSlot{functionCall}
   145  	FunctionType.slots.Get = &getSlot{functionGet}
   146  	FunctionType.slots.Repr = &unaryOpSlot{functionRepr}
   147  }
   148  
   149  // staticMethod represents Python 'staticmethod' objects.
   150  type staticMethod struct {
   151  	Object
   152  	callable *Object
   153  }
   154  
   155  func newStaticMethod(callable *Object) *staticMethod {
   156  	return &staticMethod{Object{typ: StaticMethodType}, callable}
   157  }
   158  
   159  func toStaticMethodUnsafe(o *Object) *staticMethod {
   160  	return (*staticMethod)(o.toPointer())
   161  }
   162  
   163  // ToObject upcasts f to an Object.
   164  func (m *staticMethod) ToObject() *Object {
   165  	return &m.Object
   166  }
   167  
   168  func staticMethodGet(f *Frame, desc, _ *Object, _ *Type) (*Object, *BaseException) {
   169  	m := toStaticMethodUnsafe(desc)
   170  	if m.callable == nil {
   171  		return nil, f.RaiseType(RuntimeErrorType, "uninitialized staticmethod object")
   172  	}
   173  	return m.callable, nil
   174  }
   175  
   176  func staticMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) {
   177  	if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil {
   178  		return nil, raised
   179  	}
   180  	toStaticMethodUnsafe(o).callable = args[0]
   181  	return None, nil
   182  }
   183  
   184  func initStaticMethodType(map[string]*Object) {
   185  	StaticMethodType.slots.Get = &getSlot{staticMethodGet}
   186  	StaticMethodType.slots.Init = &initSlot{staticMethodInit}
   187  }
   188  
   189  // classMethod represents Python 'classmethod' objects.
   190  type classMethod struct {
   191  	Object
   192  	callable *Object
   193  }
   194  
   195  func newClassMethod(callable *Object) *classMethod {
   196  	return &classMethod{Object{typ: ClassMethodType}, callable}
   197  }
   198  
   199  func toClassMethodUnsafe(o *Object) *classMethod {
   200  	return (*classMethod)(o.toPointer())
   201  }
   202  
   203  // ToObject upcasts f to an Object.
   204  func (m *classMethod) ToObject() *Object {
   205  	return &m.Object
   206  }
   207  
   208  func classMethodGet(f *Frame, desc, _ *Object, owner *Type) (*Object, *BaseException) {
   209  	m := toClassMethodUnsafe(desc)
   210  	if m.callable == nil {
   211  		return nil, f.RaiseType(RuntimeErrorType, "uninitialized classmethod object")
   212  	}
   213  	args := f.MakeArgs(3)
   214  	args[0] = m.callable
   215  	args[1] = owner.ToObject()
   216  	args[2] = args[1]
   217  	ret, raised := MethodType.Call(f, args, nil)
   218  	f.FreeArgs(args)
   219  	return ret, raised
   220  }
   221  
   222  func classMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) {
   223  	if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil {
   224  		return nil, raised
   225  	}
   226  	toClassMethodUnsafe(o).callable = args[0]
   227  	return None, nil
   228  }
   229  
   230  func initClassMethodType(map[string]*Object) {
   231  	ClassMethodType.slots.Get = &getSlot{classMethodGet}
   232  	ClassMethodType.slots.Init = &initSlot{classMethodInit}
   233  }