github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/bas/func.go (about)

     1  package bas
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/coyove/nj/internal"
    12  	"github.com/coyove/nj/typ"
    13  )
    14  
    15  type funcbody struct {
    16  	name      string
    17  	codeSeg   internal.Packet
    18  	stackSize uint16
    19  	numArgs   byte
    20  	varg      bool
    21  	method    bool
    22  	native    func(env *Env)
    23  	top       *Program
    24  	locals    []string
    25  	caps      []string
    26  	jumps     map[string]int
    27  }
    28  
    29  type Program struct {
    30  	stack     *[]Value
    31  	main      *Object
    32  	symbols   *Map
    33  	functions *Map
    34  	stopped   bool
    35  
    36  	File         string
    37  	Source       string
    38  	MaxStackSize int64
    39  	Globals      Map
    40  	Stdout       io.Writer
    41  	Stderr       io.Writer
    42  	Stdin        io.Reader
    43  }
    44  
    45  var topSymbols struct {
    46  	sym   Map
    47  	store Map
    48  	stack []Value
    49  }
    50  
    51  func GetTopIndex(v Value) int {
    52  	x, ok := topSymbols.sym.Get(v)
    53  	if !ok {
    54  		return 0
    55  	}
    56  	return int(x.UnsafeInt64())
    57  
    58  }
    59  
    60  func TopSymbols() Map {
    61  	return topSymbols.store.Copy()
    62  }
    63  
    64  func AddTopValue(k string, v Value) {
    65  	sk := Str(k)
    66  	idx, ok := topSymbols.sym.Get(sk)
    67  	if ok {
    68  		topSymbols.stack[idx.Int()] = v
    69  	} else {
    70  		idx := len(topSymbols.stack)
    71  		topSymbols.sym.Set(sk, Int(idx))
    72  		topSymbols.stack = append(topSymbols.stack, v)
    73  	}
    74  	topSymbols.store.Set(sk, v)
    75  }
    76  
    77  func AddTopFunc(k string, f func(*Env)) {
    78  	AddTopValue(k, Func(k, f))
    79  }
    80  
    81  // Func creates a callable object
    82  func Func(name string, f func(*Env)) Value {
    83  	if name == "" {
    84  		name = internal.UnnamedFunc()
    85  	}
    86  	if f == nil {
    87  		f = func(*Env) {}
    88  	}
    89  	obj := NewObject(0)
    90  	obj.fun = &funcbody{name: name, native: f}
    91  	obj.SetPrototype(&Proto.Func)
    92  	return obj.ToValue()
    93  }
    94  
    95  func (p *Program) Run() (v1 Value) {
    96  	p.stopped = false
    97  	if p.MaxStackSize <= 0 {
    98  		p.MaxStackSize = math.MaxInt64
    99  	}
   100  
   101  	newEnv := Env{
   102  		top:   p,
   103  		stack: p.stack,
   104  	}
   105  	defer catchPanicError(&newEnv, &v1)
   106  	v1 = internalExecCursorLoop(newEnv, p.main, nil)
   107  	return
   108  }
   109  
   110  // Stop stops the program from running unsafely, it can't stop any Go-native functions or goroutines.
   111  func (p *Program) Stop() {
   112  	p.stopped = true
   113  	return
   114  }
   115  
   116  func (p *Program) GoString() string {
   117  	x := &bytes.Buffer{}
   118  	p.main.printAll(x)
   119  	p.functions.Foreach(func(f Value, idx *Value) bool {
   120  		x.WriteByte('\n')
   121  		(*p.stack)[idx.Int()&typ.RegLocalMask].Object().printAll(x)
   122  		return true
   123  	})
   124  	return x.String()
   125  }
   126  
   127  func (p *Program) Get(k string) (v Value, ok bool) {
   128  	addr, ok := p.symbols.Get(Str(k))
   129  	if !ok {
   130  		return Nil, false
   131  	}
   132  	return (*p.stack)[addr.Int64()], true
   133  }
   134  
   135  func (p *Program) Set(k string, v Value) (ok bool) {
   136  	addr, ok := p.symbols.Get(Str(k))
   137  	if !ok {
   138  		return false
   139  	}
   140  	(*p.stack)[addr.Int64()] = v
   141  	return true
   142  }
   143  
   144  func (p *Program) LocalsObject() *Object {
   145  	r := NewObject(len(p.main.fun.locals))
   146  	for i, name := range p.main.fun.locals {
   147  		r.Set(Str(name), (*p.stack)[i])
   148  	}
   149  	return r
   150  }
   151  
   152  // Apply calls the object with provided 'this' value, 'e' is for stacktracing and is optional.
   153  func (m *Object) Apply(e *Env, this Value, args ...Value) Value {
   154  	return callobj(m, e.getStacktraces(), e.getTop(), false, this, args...)
   155  }
   156  
   157  // TryApply calls the object with provided 'this' value, 'e' is for stacktracing and is optional.
   158  // Panic will be recovered and returned as an error.
   159  func (m *Object) TryApply(e *Env, this Value, args ...Value) (res Value) {
   160  	return callobj(m, e.getStacktraces(), e.getTop(), true, this, args...)
   161  }
   162  
   163  // Call calls the object, 'e' is for stacktracing and is optional.
   164  func (m *Object) Call(e *Env, args ...Value) (res Value) {
   165  	return callobj(m, e.getStacktraces(), e.getTop(), false, m.this, args...)
   166  }
   167  
   168  // TryCall calls the object, 'e' is for stacktracing and is optional. Panic will be recovered and returned as an error.
   169  func (m *Object) TryCall(e *Env, args ...Value) (res Value) {
   170  	return callobj(m, e.getStacktraces(), e.getTop(), true, m.this, args...)
   171  }
   172  
   173  func callobj(m *Object, r stacktraces, g *Program, outErr bool, this Value, args ...Value) (res Value) {
   174  	c := m.fun
   175  	newEnv := Env{
   176  		A:     this,
   177  		top:   c.top,
   178  		stack: &args,
   179  	}
   180  
   181  	if c.top == nil {
   182  		newEnv.top = g
   183  	}
   184  
   185  	if outErr {
   186  		defer catchPanicError(&newEnv, &res)
   187  	}
   188  
   189  	if c.native != nil {
   190  		defer relayPanic(func() []Stacktrace { return newEnv.runtime.Stacktrace(false) })
   191  		newEnv.runtime = r.push(Stacktrace{
   192  			Callable: m,
   193  		})
   194  		c.native(&newEnv)
   195  		return newEnv.A
   196  	}
   197  
   198  	if c.varg {
   199  		s := *newEnv.stack
   200  		if len(s) > int(c.numArgs)-1 {
   201  			s[c.numArgs-1] = newVarargArray(s[c.numArgs-1:]).ToValue()
   202  		} else {
   203  			if newEnv.Size() < int(c.numArgs)-1 {
   204  				panicNotEnoughArgs(m)
   205  			}
   206  			newEnv.resize(int(c.numArgs))
   207  			newEnv._set(uint16(c.numArgs)-1, Nil)
   208  		}
   209  	} else {
   210  		if newEnv.Size() < int(c.numArgs) {
   211  			panicNotEnoughArgs(m)
   212  		}
   213  	}
   214  	newEnv.resizeZero(int(c.stackSize), int(c.numArgs))
   215  
   216  	return internalExecCursorLoop(newEnv, m, r.Stacktrace(false))
   217  }
   218  
   219  func (o *Object) funcSig() string {
   220  	c := o.fun
   221  	p := bytes.NewBufferString(c.name)
   222  	p.WriteString(internal.IfStr(c.method, "({this},", "("))
   223  	if c.native != nil {
   224  		p.WriteString("...")
   225  	} else {
   226  		for i := 0; i < int(c.numArgs); i++ {
   227  			fmt.Fprintf(p, "a%d,", i)
   228  		}
   229  	}
   230  	internal.CloseBuffer(p, internal.IfStr(c.varg, "...)", ")"))
   231  	return p.String()
   232  }
   233  
   234  func (obj *Object) GoString() string {
   235  	buf := &bytes.Buffer{}
   236  	obj.printAll(buf)
   237  	return buf.String()
   238  }
   239  
   240  func (obj *Object) printAll(w io.Writer) {
   241  	cls, p := obj.fun, obj.fun.top
   242  	internal.WriteString(w, "start)\t"+obj.funcSig()+"\n")
   243  	if obj.parent != nil {
   244  		internal.WriteString(w, "proto)\t"+obj.parent.Name()+"\n")
   245  	}
   246  	if cls.native != nil {
   247  		internal.WriteString(w, "0)\t0\tnative code\n")
   248  	} else {
   249  		if cls == p.main.fun {
   250  			internal.WriteString(w, "source)\t"+cls.top.File+"\n")
   251  		}
   252  
   253  		readAddr := func(a uint16, rValue bool) string {
   254  			if a == typ.RegA {
   255  				return "a"
   256  			}
   257  
   258  			suffix := ""
   259  			if addr := a & typ.RegLocalMask; a != addr && rValue && int(addr) < len(*p.stack) {
   260  				x := (*p.stack)[addr]
   261  				if x != Nil {
   262  					suffix = ":" + x.simple()
   263  				}
   264  			}
   265  
   266  			if a > typ.RegLocalMask {
   267  				return fmt.Sprintf("g%d", a&typ.RegLocalMask) + suffix
   268  			}
   269  			return fmt.Sprintf("sp+%d", a&typ.RegLocalMask) + suffix
   270  		}
   271  
   272  		oldpos := cls.codeSeg.Pos
   273  
   274  		var cursor uint32
   275  		for cursor < uint32(len(cls.codeSeg.Code)) {
   276  			inst := cls.codeSeg.Code[cursor]
   277  			cursor++
   278  			bop, a, b, c := inst.Opcode, inst.A, inst.B, inst.C
   279  
   280  			if oldpos.Len() > 0 {
   281  				c1 := cursor - 1
   282  				_, op, line := oldpos.Read(0)
   283  				for uint32(c1) > op && oldpos.Len() > 0 {
   284  					op, line = oldpos.Pop()
   285  				}
   286  				internal.WriteString(w, fmt.Sprintf("%d)\t%d\t", line, c1))
   287  			} else {
   288  				internal.WriteString(w, fmt.Sprintf("$)\t%d\t", cursor-1))
   289  			}
   290  
   291  			switch bop {
   292  			case typ.OpSet:
   293  				fmt.Fprintf(w, "%s = %s", readAddr(a, false), readAddr(b, false))
   294  			case typ.OpCreateArray:
   295  				internal.WriteString(w, "createarray")
   296  			case typ.OpCreateObject:
   297  				internal.WriteString(w, "createobject")
   298  			case typ.OpFunction:
   299  				if a == typ.RegA {
   300  					internal.WriteString(w, "loadself")
   301  				} else if b == 0 {
   302  					fmt.Fprintf(w, "%s = loadfunction %s", readAddr(c, false), readAddr(a, true))
   303  				} else if b == 1 {
   304  					fmt.Fprintf(w, "%s = loadclosure %s", readAddr(c, false), readAddr(a, true))
   305  				}
   306  			case typ.OpTailCall:
   307  				if inst.OpcodeExt == 1 {
   308  					fmt.Fprintf(w, "tailfastcall %v %v", readAddr(a, true), readAddr(b, false))
   309  				} else if inst.OpcodeExt == 2 {
   310  					fmt.Fprintf(w, "tailfastcall %v %v %v", readAddr(a, true), readAddr(b, false), readAddr(c, false))
   311  				} else {
   312  					fmt.Fprintf(w, "tailcall %v", readAddr(a, true))
   313  				}
   314  			case typ.OpCall:
   315  				if inst.OpcodeExt == 1 {
   316  					fmt.Fprintf(w, "fastcall %v %v", readAddr(a, true), readAddr(b, false))
   317  				} else if inst.OpcodeExt == 2 {
   318  					fmt.Fprintf(w, "fastcall %v %v %v", readAddr(a, true), readAddr(b, false), readAddr(c, false))
   319  				} else {
   320  					fmt.Fprintf(w, "call %v", readAddr(a, true))
   321  				}
   322  			case typ.OpJmpFalse:
   323  				fmt.Fprintf(w, "jmpfalse %d", uint32(int32(cursor)+inst.D()))
   324  			case typ.OpJmp:
   325  				fmt.Fprintf(w, "jmp %d", uint32(int32(cursor)+inst.D()))
   326  			case typ.OpInc:
   327  				if c != 0 {
   328  					fmt.Fprintf(w, "inc %s %s jmp %d", readAddr(a, false), readAddr(b, false), int32(cursor)+int32(int16(c)))
   329  				} else {
   330  					fmt.Fprintf(w, "inc %s %s", readAddr(a, false), readAddr(b, false))
   331  				}
   332  			case typ.OpLoad:
   333  				fmt.Fprintf(w, "%s = %s[%s]", readAddr(c, false), readAddr(a, false), readAddr(b, false))
   334  			case typ.OpStore:
   335  				fmt.Fprintf(w, "%s[%s] = %s", readAddr(a, false), readAddr(b, false), readAddr(c, false))
   336  			case typ.OpSlice:
   337  				fmt.Fprintf(w, "sliceload %s %s %s", readAddr(a, false), readAddr(b, false), readAddr(c, false))
   338  			case typ.OpLoadTop:
   339  				if b != typ.RegPhantom {
   340  					fmt.Fprintf(w, "%s = loadtop %s[%s]", readAddr(c, false), topSymbols.stack[a].simple(), readAddr(b, true))
   341  				} else {
   342  					fmt.Fprintf(w, "%s = loadtop %s", readAddr(c, false), topSymbols.stack[a].simple())
   343  				}
   344  			case typ.OpExt:
   345  				switch inst.OpcodeExt {
   346  				case typ.OpExtAdd16:
   347  					fmt.Fprintf(w, "add %s $%d", readAddr(a, false), int16(b))
   348  				case typ.OpExtRSub16:
   349  					fmt.Fprintf(w, "sub $%d %s", int16(b), readAddr(a, false))
   350  				case typ.OpExtLess16:
   351  					fmt.Fprintf(w, "less %s $%d", readAddr(a, false), int16(b))
   352  				case typ.OpExtGreat16:
   353  					fmt.Fprintf(w, "less $%d %s", int16(b), readAddr(a, false))
   354  				case typ.OpExtEq16:
   355  					fmt.Fprintf(w, "eq %s $%d", readAddr(a, false), int16(b))
   356  				case typ.OpExtNeq16:
   357  					fmt.Fprintf(w, "neq %s $%d", readAddr(a, false), int16(b))
   358  				case typ.OpExtInc16:
   359  					if c != 0 {
   360  						fmt.Fprintf(w, "inc %s $%d jmp %d", readAddr(a, false), int16(b), int32(cursor)+int32(int16(c)))
   361  					} else {
   362  						fmt.Fprintf(w, "inc %s $%d", readAddr(a, false), int16(b))
   363  					}
   364  				case typ.OpExtLoad16:
   365  					fmt.Fprintf(w, "%s = %s[$%d]", readAddr(c, false), readAddr(a, false), int16(b))
   366  				case typ.OpExtStore16:
   367  					fmt.Fprintf(w, "%s[$%d] = %s", readAddr(a, false), int16(b), readAddr(c, false))
   368  				case typ.OpExtBitAnd:
   369  					fmt.Fprintf(w, "bitand %s %s", readAddr(a, false), readAddr(b, false))
   370  				case typ.OpExtBitOr:
   371  					fmt.Fprintf(w, "bitor %s %s", readAddr(a, false), readAddr(b, false))
   372  				case typ.OpExtBitXor:
   373  					fmt.Fprintf(w, "bitxor %s %s", readAddr(a, false), readAddr(b, false))
   374  				case typ.OpExtBitLsh:
   375  					fmt.Fprintf(w, "bitlsh %s %s", readAddr(a, false), readAddr(b, false))
   376  				case typ.OpExtBitRsh:
   377  					fmt.Fprintf(w, "bitrsh %s %s", readAddr(a, false), readAddr(b, false))
   378  				case typ.OpExtBitURsh:
   379  					fmt.Fprintf(w, "bitursh %s %s", readAddr(a, false), readAddr(b, false))
   380  				case typ.OpExtBitAnd16:
   381  					fmt.Fprintf(w, "bitand %s $%d", readAddr(a, false), int16(b))
   382  				case typ.OpExtBitOr16:
   383  					fmt.Fprintf(w, "bitor %s $%d", readAddr(a, false), int16(b))
   384  				case typ.OpExtBitXor16:
   385  					fmt.Fprintf(w, "bitxor %s $%d", readAddr(a, false), int16(b))
   386  				case typ.OpExtBitLsh16:
   387  					fmt.Fprintf(w, "bitlsh %s $%d", readAddr(a, false), int16(b))
   388  				case typ.OpExtBitRsh16:
   389  					fmt.Fprintf(w, "bitrsh %s $%d", readAddr(a, false), int16(b))
   390  				case typ.OpExtBitURsh16:
   391  					fmt.Fprintf(w, "bitursh %s $%d", readAddr(a, false), int16(b))
   392  				default:
   393  					fmt.Fprintf(w, "? %02x", inst.OpcodeExt)
   394  				}
   395  			default:
   396  				if us, ok := typ.UnaryOpcode[bop]; ok {
   397  					fmt.Fprintf(w, "%v %v", us, readAddr(a, false))
   398  				} else if bs, ok := typ.BinaryOpcode[bop]; ok {
   399  					fmt.Fprintf(w, "%v %v %v", bs, readAddr(a, false), readAddr(b, false))
   400  				} else {
   401  					fmt.Fprintf(w, "? %02x", bop)
   402  				}
   403  			}
   404  
   405  			internal.WriteString(w, "\n")
   406  		}
   407  	}
   408  	ki := 0
   409  	obj.Foreach(func(k Value, v *Value) bool {
   410  		fmt.Fprintf(w, "prop%d)\t%v\t%v\n", ki, k, *v)
   411  		ki++
   412  		return true
   413  	})
   414  	internal.WriteString(w, "end)\t"+obj.funcSig())
   415  }
   416  
   417  func NewBareFunc(f string, varg bool, np byte, ss uint16,
   418  	locals, caps []string, jt map[string]int, code internal.Packet) *Object {
   419  	obj := NewObject(0)
   420  	obj.SetPrototype(&Proto.Func)
   421  	obj.fun = &funcbody{}
   422  	obj.fun.varg = varg
   423  	obj.fun.numArgs = np
   424  	obj.fun.name = f
   425  	obj.fun.stackSize = ss
   426  	obj.fun.codeSeg = code
   427  	obj.fun.locals = locals
   428  	obj.fun.method = strings.Contains(f, ".")
   429  	obj.fun.caps = caps
   430  	obj.fun.jumps = jt
   431  	return obj
   432  }
   433  
   434  func NewBareProgram(coreStack []Value, top *Object, symbols, funcs *Map) *Program {
   435  	cls := &Program{}
   436  	cls.main = top
   437  	cls.stack = &coreStack
   438  	cls.symbols = symbols
   439  	cls.functions = funcs
   440  	cls.Stdout = os.Stdout
   441  	cls.Stdin = os.Stdin
   442  	cls.Stderr = os.Stderr
   443  
   444  	cls.main.fun.top = cls
   445  	cls.functions.Foreach(func(f Value, idx *Value) bool {
   446  		(*cls.stack)[idx.Int()&typ.RegLocalMask].Object().fun.top = cls
   447  		return true
   448  	})
   449  	return cls
   450  }