github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/runtime/gocont.go (about)

     1  package runtime
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"unsafe"
     7  )
     8  
     9  // GoCont implements Cont for functions written in Go.
    10  type GoCont struct {
    11  	*GoFunction
    12  	next  Cont
    13  	args  []Value
    14  	etc   *[]Value
    15  	nArgs int
    16  }
    17  
    18  var _ Cont = (*GoCont)(nil)
    19  
    20  // NewGoCont returns a new pointer to GoCont for the given GoFunction and Cont.
    21  func NewGoCont(t *Thread, f *GoFunction, next Cont) *GoCont {
    22  	var args []Value
    23  	var etc *[]Value
    24  	if f.nArgs > 0 {
    25  		t.RequireArrSize(unsafe.Sizeof(Value{}), f.nArgs)
    26  		args = t.argsPool.get(f.nArgs)
    27  	}
    28  	if f.hasEtc {
    29  		etc = new([]Value)
    30  	}
    31  	t.RequireSize(unsafe.Sizeof(GoCont{}))
    32  	cont := t.goContPool.get()
    33  	*cont = GoCont{
    34  		GoFunction: f,
    35  		args:       args,
    36  		etc:        etc,
    37  		next:       next,
    38  	}
    39  	return cont
    40  }
    41  
    42  // Push implements Cont.Push.
    43  func (c *GoCont) Push(r *Runtime, v Value) {
    44  	if c.nArgs < len(c.args) {
    45  		c.args[c.nArgs] = v
    46  		c.nArgs++
    47  	} else if c.etc != nil {
    48  		r.RequireSize(unsafe.Sizeof(Value{}))
    49  		*c.etc = append(*c.etc, v)
    50  	}
    51  }
    52  
    53  // PushingNext is convenient when implementing go functions.  It pushes the
    54  // given values to c.Next() and returns it.
    55  func (c *GoCont) PushingNext(r *Runtime, vals ...Value) Cont {
    56  	next := c.Next()
    57  	next.PushEtc(r, vals)
    58  	return next
    59  }
    60  
    61  // PushingNext1 is convenient when implementing go functions.  It pushes the
    62  // given value to c.Next() and returns it.
    63  func (c *GoCont) PushingNext1(r *Runtime, val Value) Cont {
    64  	next := c.Next()
    65  	next.Push(r, val)
    66  	return next
    67  }
    68  
    69  // PushEtc pushes a slice of values to the continutation.
    70  func (c *GoCont) PushEtc(r *Runtime, etc []Value) {
    71  	if c.nArgs < len(c.args) {
    72  		for i, v := range etc {
    73  			c.args[c.nArgs] = v
    74  			c.nArgs++
    75  			if c.nArgs == len(c.args) {
    76  				etc = etc[i+1:]
    77  				goto FillEtc
    78  			}
    79  		}
    80  		return
    81  	}
    82  FillEtc:
    83  	if c.etc == nil {
    84  		return
    85  	}
    86  	r.RequireArrSize(unsafe.Sizeof(Value{}), len(etc))
    87  	*c.etc = append(*c.etc, etc...)
    88  }
    89  
    90  // RunInThread implements Cont.RunInThread.
    91  func (c *GoCont) RunInThread(t *Thread) (next Cont, err error) {
    92  	if err := t.CheckRequiredFlags(c.safetyFlags); err != nil {
    93  		return nil, err
    94  	}
    95  	t.RequireCPU(1)
    96  
    97  	t.goFunctionCallDepth++
    98  	defer func() { t.goFunctionCallDepth-- }()
    99  
   100  	if t.goFunctionCallDepth > maxGoFunctionCallDepth {
   101  		return nil, errors.New("stack overflow")
   102  	}
   103  	next, err = c.f(t, c)
   104  	_ = t.triggerReturn(t, c)
   105  
   106  	if err != nil {
   107  		// If there is an error, c is still potentially needed for error
   108  		// handling, so do not return it to the pool.  It will get GCed when no
   109  		// longer referenced, so it's OK.
   110  		return
   111  	}
   112  	if c.args != nil {
   113  		t.ReleaseArrSize(unsafe.Sizeof(Value{}), c.nArgs)
   114  		t.argsPool.release(c.args)
   115  	}
   116  	t.ReleaseSize(unsafe.Sizeof(GoCont{}))
   117  	t.goContPool.release(c)
   118  	return
   119  }
   120  
   121  // Next returns the next continuation.
   122  func (c *GoCont) Next() Cont {
   123  	return c.next
   124  }
   125  
   126  // Parent returns the continuation's parent.
   127  func (c *GoCont) Parent() Cont {
   128  	return c.next
   129  }
   130  
   131  // DebugInfo returns c's debug info.
   132  func (c *GoCont) DebugInfo() *DebugInfo {
   133  	name := c.name
   134  	if name == "" {
   135  		name = "<go function>"
   136  	}
   137  	return &DebugInfo{
   138  		Source:      "[Go]",
   139  		CurrentLine: 0,
   140  		Name:        name,
   141  	}
   142  }
   143  
   144  // NArgs returns the number of args pushed to the continuation.
   145  func (c *GoCont) NArgs() int {
   146  	return c.nArgs
   147  }
   148  
   149  // Arg returns the n-th arg of the continuation.  It doesn't do any range check!
   150  func (c *GoCont) Arg(n int) Value {
   151  	return c.args[n]
   152  }
   153  
   154  // Args returns the slice of args pushed to the continuation.
   155  func (c *GoCont) Args() []Value {
   156  	return c.args[:c.nArgs]
   157  }
   158  
   159  // Etc returns the etc args pushed to the continuation they exist.
   160  func (c *GoCont) Etc() []Value {
   161  	if c.etc == nil {
   162  		return nil
   163  	}
   164  	return *c.etc
   165  }
   166  
   167  // Check1Arg returns a non-nil error if the continuation doesn't have at least
   168  // one arg.
   169  func (c *GoCont) Check1Arg() error {
   170  	if c.nArgs == 0 {
   171  		return errors.New("bad argument #1 (value needed)")
   172  	}
   173  	return nil
   174  }
   175  
   176  // CheckNArgs returns a non-nil error if the continuation doesn't have at least
   177  // n args.
   178  func (c *GoCont) CheckNArgs(n int) error {
   179  	if c.nArgs < n {
   180  		return fmt.Errorf("%d arguments needed", n)
   181  	}
   182  	return nil
   183  }
   184  
   185  // StringArg returns the n-th argument as a string if possible, otherwise a
   186  // non-nil error.  No range check!
   187  func (c *GoCont) StringArg(n int) (string, error) {
   188  	s, ok := c.Arg(n).TryString()
   189  	if !ok {
   190  		return "", fmt.Errorf("#%d must be a string", n+1)
   191  	}
   192  	return s, nil
   193  }
   194  
   195  // BoolArg returns the n-th argument as a string if possible, otherwise a
   196  // non-nil error.  No range check!
   197  func (c *GoCont) BoolArg(n int) (bool, error) {
   198  	arg := c.Arg(n)
   199  	if arg.IsNil() {
   200  		return false, nil
   201  	}
   202  	b, ok := arg.TryBool()
   203  	if !ok {
   204  		return false, fmt.Errorf("#%d must be a boolean", n+1)
   205  	}
   206  	return b, nil
   207  }
   208  
   209  // CallableArg returns the n-th argument as a callable if possible, otherwise a
   210  // non-nil error.  No range check!
   211  func (c *GoCont) CallableArg(n int) (Callable, error) {
   212  	f, ok := c.Arg(n).TryCallable()
   213  	if !ok {
   214  		return nil, fmt.Errorf("#%d must be a callable", n+1)
   215  	}
   216  	return f, nil
   217  }
   218  
   219  // ClosureArg returns the n-th argument as a closure if possible, otherwise a
   220  // non-nil error.  No range check!
   221  func (c *GoCont) ClosureArg(n int) (*Closure, error) {
   222  	f, ok := c.Arg(n).TryClosure()
   223  	if !ok {
   224  		return nil, fmt.Errorf("#%d must be a lua function", n+1)
   225  	}
   226  	return f, nil
   227  }
   228  
   229  // ThreadArg returns the n-th argument as a thread if possible, otherwise a
   230  // non-nil error.  No range check!
   231  func (c *GoCont) ThreadArg(n int) (*Thread, error) {
   232  	t, ok := c.Arg(n).TryThread()
   233  	if !ok {
   234  		return nil, fmt.Errorf("#%d must be a thread", n+1)
   235  	}
   236  	return t, nil
   237  }
   238  
   239  // IntArg returns the n-th argument as an Int if possible, otherwise a
   240  // non-nil error.  No range check!
   241  func (c *GoCont) IntArg(n int) (int64, error) {
   242  	i, ok := ToInt(c.Arg(n))
   243  	if !ok {
   244  		return 0, fmt.Errorf("#%d must be an integer", n+1)
   245  	}
   246  	return i, nil
   247  }
   248  
   249  // FloatArg returns the n-th argument as a Float if possible, otherwise a
   250  // non-nil error.  No range check!
   251  func (c *GoCont) FloatArg(n int) (float64, error) {
   252  	x, ok := ToFloat(c.Arg(n))
   253  	if !ok {
   254  		return 0, fmt.Errorf("#%d must be a number", n+1)
   255  	}
   256  	return x, nil
   257  }
   258  
   259  // TableArg returns the n-th argument as a table if possible, otherwise a
   260  // non-nil error.  No range check!
   261  func (c *GoCont) TableArg(n int) (*Table, error) {
   262  	t, ok := c.Arg(n).TryTable()
   263  	if !ok {
   264  		return nil, fmt.Errorf("#%d must be a table", n+1)
   265  	}
   266  	return t, nil
   267  }
   268  
   269  // UserDataArg returns the n-th argument as a UserData if possible, otherwise a
   270  // non-nil error.  No range check!
   271  func (c *GoCont) UserDataArg(n int) (*UserData, error) {
   272  	t, ok := c.Arg(n).TryUserData()
   273  	if !ok {
   274  		return nil, fmt.Errorf("#%d must be userdata", n+1)
   275  	}
   276  	return t, nil
   277  }