github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/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  	return &Function{Object{typ: FunctionType, dict: NewDict()}, nil, c.name, c, globals}
    93  }
    94  
    95  // newBuiltinFunction returns a function object with the given name that
    96  // invokes fn when called.
    97  func newBuiltinFunction(name string, fn Func) *Function {
    98  	return &Function{Object: Object{typ: FunctionType, dict: NewDict()}, fn: fn, name: name}
    99  }
   100  
   101  func toFunctionUnsafe(o *Object) *Function {
   102  	return (*Function)(o.toPointer())
   103  }
   104  
   105  // ToObject upcasts f to an Object.
   106  func (f *Function) ToObject() *Object {
   107  	return &f.Object
   108  }
   109  
   110  // Name returns f's name field.
   111  func (f *Function) Name() string {
   112  	return f.name
   113  }
   114  
   115  func functionCall(f *Frame, callable *Object, args Args, kwargs KWArgs) (*Object, *BaseException) {
   116  	fun := toFunctionUnsafe(callable)
   117  	code := fun.code
   118  	if code == nil {
   119  		return fun.fn(f, args, kwargs)
   120  	}
   121  	return code.Eval(f, fun.globals, args, kwargs)
   122  }
   123  
   124  func functionGet(f *Frame, desc, instance *Object, owner *Type) (*Object, *BaseException) {
   125  	args := f.MakeArgs(3)
   126  	args[0] = desc
   127  	args[1] = instance
   128  	args[2] = owner.ToObject()
   129  	ret, raised := MethodType.Call(f, args, nil)
   130  	f.FreeArgs(args)
   131  	return ret, raised
   132  }
   133  
   134  func functionRepr(_ *Frame, o *Object) (*Object, *BaseException) {
   135  	fun := toFunctionUnsafe(o)
   136  	return NewStr(fmt.Sprintf("<%s %s at %p>", fun.typ.Name(), fun.Name(), fun)).ToObject(), nil
   137  }
   138  
   139  func initFunctionType(map[string]*Object) {
   140  	FunctionType.flags &= ^(typeFlagInstantiable | typeFlagBasetype)
   141  	FunctionType.slots.Call = &callSlot{functionCall}
   142  	FunctionType.slots.Get = &getSlot{functionGet}
   143  	FunctionType.slots.Repr = &unaryOpSlot{functionRepr}
   144  }
   145  
   146  // staticMethod represents Python 'staticmethod' objects.
   147  type staticMethod struct {
   148  	Object
   149  	callable *Object
   150  }
   151  
   152  func newStaticMethod(callable *Object) *staticMethod {
   153  	return &staticMethod{Object{typ: StaticMethodType}, callable}
   154  }
   155  
   156  func toStaticMethodUnsafe(o *Object) *staticMethod {
   157  	return (*staticMethod)(o.toPointer())
   158  }
   159  
   160  // ToObject upcasts f to an Object.
   161  func (m *staticMethod) ToObject() *Object {
   162  	return &m.Object
   163  }
   164  
   165  func staticMethodGet(f *Frame, desc, _ *Object, _ *Type) (*Object, *BaseException) {
   166  	m := toStaticMethodUnsafe(desc)
   167  	if m.callable == nil {
   168  		return nil, f.RaiseType(RuntimeErrorType, "uninitialized staticmethod object")
   169  	}
   170  	return m.callable, nil
   171  }
   172  
   173  func staticMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) {
   174  	if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil {
   175  		return nil, raised
   176  	}
   177  	toStaticMethodUnsafe(o).callable = args[0]
   178  	return None, nil
   179  }
   180  
   181  func initStaticMethodType(map[string]*Object) {
   182  	StaticMethodType.slots.Get = &getSlot{staticMethodGet}
   183  	StaticMethodType.slots.Init = &initSlot{staticMethodInit}
   184  }
   185  
   186  // classMethod represents Python 'classmethod' objects.
   187  type classMethod struct {
   188  	Object
   189  	callable *Object
   190  }
   191  
   192  func newClassMethod(callable *Object) *classMethod {
   193  	return &classMethod{Object{typ: ClassMethodType}, callable}
   194  }
   195  
   196  func toClassMethodUnsafe(o *Object) *classMethod {
   197  	return (*classMethod)(o.toPointer())
   198  }
   199  
   200  // ToObject upcasts f to an Object.
   201  func (m *classMethod) ToObject() *Object {
   202  	return &m.Object
   203  }
   204  
   205  func classMethodGet(f *Frame, desc, _ *Object, owner *Type) (*Object, *BaseException) {
   206  	m := toClassMethodUnsafe(desc)
   207  	if m.callable == nil {
   208  		return nil, f.RaiseType(RuntimeErrorType, "uninitialized classmethod object")
   209  	}
   210  	args := f.MakeArgs(3)
   211  	args[0] = m.callable
   212  	args[1] = owner.ToObject()
   213  	args[2] = args[1]
   214  	ret, raised := MethodType.Call(f, args, nil)
   215  	f.FreeArgs(args)
   216  	return ret, raised
   217  }
   218  
   219  func classMethodInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) {
   220  	if raised := checkFunctionArgs(f, "__init__", args, ObjectType); raised != nil {
   221  		return nil, raised
   222  	}
   223  	toClassMethodUnsafe(o).callable = args[0]
   224  	return None, nil
   225  }
   226  
   227  func initClassMethodType(map[string]*Object) {
   228  	ClassMethodType.slots.Get = &getSlot{classMethodGet}
   229  	ClassMethodType.slots.Init = &initSlot{classMethodInit}
   230  }