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

     1  package bas
     2  
     3  import (
     4  	"bytes"
     5  	"unsafe"
     6  
     7  	"github.com/coyove/nj/internal"
     8  	"github.com/coyove/nj/typ"
     9  )
    10  
    11  // Env is the environment for a function to run within.
    12  // 'stack' represents the global stack, a running function use 'stack[stackOffset:]' as its local stack.
    13  // 'A' stores the result of the execution. 'global' is the topmost function scope, a.k.a. Program.
    14  type Env struct {
    15  	stack       *[]Value
    16  	top         *Program
    17  	A           Value
    18  	stackOffset uint32
    19  	runtime     stacktraces
    20  }
    21  
    22  type stacktraces struct {
    23  	// Stacktrace layout: N, N-1, ..., 2, 1, 0(current)
    24  	stackN []Stacktrace // [N, N-1, ..., 2]
    25  	stack1 Stacktrace   // 1. If nil, then 'stack0' is the only one in stacktrace
    26  	stack0 Stacktrace   // 0
    27  }
    28  
    29  func (r stacktraces) Stacktrace(copy bool) []Stacktrace {
    30  	if r.stack0.Callable == nil {
    31  		return nil
    32  	}
    33  	if r.stack1.Callable == nil {
    34  		return []Stacktrace{r.stack0}
    35  	}
    36  	s := append(r.stackN, r.stack1, r.stack0)
    37  	if copy {
    38  		return append([]Stacktrace{}, s...)
    39  	}
    40  	return s
    41  }
    42  
    43  func (r stacktraces) push(k Stacktrace) stacktraces {
    44  	if r.stack1.Callable != nil {
    45  		r.stackN = append(r.stackN, r.stack1)
    46  	}
    47  	r.stack1 = r.stack0
    48  	r.stack0 = k
    49  	return r
    50  }
    51  
    52  func (env *Env) getTop() *Program {
    53  	if env == nil {
    54  		return nil
    55  	}
    56  	return env.top
    57  }
    58  
    59  func (env *Env) getStacktraces() stacktraces {
    60  	if env == nil {
    61  		return stacktraces{}
    62  	}
    63  	return env.runtime
    64  }
    65  
    66  func (env *Env) resizeZero(newSize, zeroSize int) {
    67  	// old := len(*env.stack)
    68  	env.resize(newSize)
    69  	// for i := old; i < int(env.stackOffset())+zeroSize; i++ {
    70  	// 	(*env.stack)[i] = Value{}
    71  	// }
    72  }
    73  
    74  func (env *Env) resize(newSize int) {
    75  	s := *env.stack
    76  	sz := int(env.stackOffset) + newSize
    77  	if sz > cap(s) {
    78  		old := s
    79  		s = make([]Value, sz+newSize)
    80  		copy(s, old)
    81  	}
    82  	*env.stack = s[:sz]
    83  }
    84  
    85  // Get gets value at 'index' in current stack, Get(-1) means env.A.
    86  func (env *Env) Get(index int) Value {
    87  	if index == -1 {
    88  		return env.A
    89  	}
    90  	s := *env.stack
    91  	index += int(env.stackOffset)
    92  	if index < len(s) {
    93  		return s[index]
    94  	}
    95  	return Nil
    96  }
    97  
    98  // Set sets 'value' at 'index' in current stack.
    99  func (env *Env) Set(index int, value Value) {
   100  	env._set(uint16(index)&typ.RegLocalMask, value)
   101  }
   102  
   103  func (env *Env) clear() {
   104  	*env.stack = (*env.stack)[:env.stackOffset]
   105  	env.A = Value{}
   106  }
   107  
   108  func (env *Env) push(v Value) {
   109  	*env.stack = append(*env.stack, v)
   110  }
   111  
   112  func (env *Env) Size() int {
   113  	return len(*env.stack) - int(env.stackOffset)
   114  }
   115  
   116  func (env *Env) _ref(yx uint16) *Value {
   117  	if yx == typ.RegA {
   118  		return &env.A
   119  	}
   120  	if yx <= typ.RegLocalMask {
   121  		offset := uintptr(uint32(yx)+env.stackOffset) * ValueSize
   122  		return (*Value)(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(env.stack)) + offset))
   123  		// return &(*env.stack)[uint32(yx)+env.stackOffset]
   124  	}
   125  	offset := uintptr(yx&typ.RegLocalMask) * ValueSize
   126  	return (*Value)(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(env.top.stack)) + offset))
   127  	// return (*env.global.stack)[yx&typ.RegLocalMask]
   128  }
   129  
   130  func (env *Env) _refgp(yx uint16) *Value {
   131  	if yx > typ.RegA {
   132  		offset := uintptr(yx&typ.RegLocalMask) * ValueSize
   133  		return (*Value)(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(env.top.stack)) + offset))
   134  		// return (*env.global.stack)[yx&typ.RegLocalMask]
   135  	}
   136  	if yx == typ.RegA {
   137  		return &env.A
   138  	}
   139  	offset := uintptr(uint32(yx)+env.stackOffset) * ValueSize
   140  	return (*Value)(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(env.stack)) + offset))
   141  	// return &(*env.stack)[uint32(yx)+env.stackOffset]
   142  }
   143  
   144  func (env *Env) _get(yx uint16) Value {
   145  	return *env._ref(yx)
   146  }
   147  
   148  func (env *Env) _set(yx uint16, v Value) {
   149  	*env._ref(yx) = v
   150  }
   151  
   152  // Stack returns current stack as a reference.
   153  func (env *Env) Stack() []Value { return (*env.stack)[env.stackOffset:] }
   154  
   155  // CopyStack returns a copy of current stack.
   156  func (env *Env) CopyStack() []Value { return append([]Value{}, env.Stack()...) }
   157  
   158  func (env *Env) String() string {
   159  	buf := bytes.NewBufferString("env(")
   160  	buf.WriteString(env.A.String())
   161  	buf.WriteString(")")
   162  	return buf.String()
   163  }
   164  
   165  // Bool returns value at 'idx' in current stack and asserts its Type() to be a boolean.
   166  func (env *Env) Bool(idx int) bool { return env.mustBe(typ.Bool, idx).Bool() }
   167  
   168  // Str returns value at 'idx' in current stack and asserts its Type() to be a string.
   169  func (env *Env) Str(idx int) string { return env.mustBe(typ.String, idx).String() }
   170  
   171  func (env *Env) StrDefault(idx int, defaultValue string, minLen int) (res string) {
   172  	v := env.Get(idx)
   173  	switch v.Type() {
   174  	case typ.String:
   175  		if v.Len() < minLen {
   176  			return defaultValue
   177  		}
   178  		return v.Str()
   179  	case typ.Nil:
   180  		return defaultValue
   181  	case typ.Native:
   182  		if buf, ok := v.Native().Unwrap().([]byte); ok {
   183  			if len(buf) < minLen {
   184  				return defaultValue
   185  			}
   186  			*(*[2]int)(unsafe.Pointer(&res)) = *(*[2]int)(unsafe.Pointer(&buf))
   187  			return
   188  		}
   189  	}
   190  	if minLen > 0 {
   191  		internal.Panic("expects argument #%d to be string and at least %db long, got %v", idx+1, minLen, v.simple())
   192  	}
   193  	internal.Panic("expects argument #%d to be string, bytes or nil, got %v", idx+1, v.simple())
   194  	return
   195  }
   196  
   197  // Num returns value at 'idx' in current stack and asserts its Type() to be a number.
   198  func (env *Env) Num(idx int) Value { return env.mustBe(typ.Number, idx) }
   199  
   200  // Int64 returns value at 'idx' in current stack and asserts its Type() to be a number.
   201  func (env *Env) Int64(idx int) int64 { return env.mustBe(typ.Number, idx).Int64() }
   202  
   203  // Int returns value at 'idx' in current stack and asserts its Type() to be a number.
   204  func (env *Env) Int(idx int) int { return env.mustBe(typ.Number, idx).Int() }
   205  
   206  // IntDefault returns value at 'idx' in current stack and asserts its Type() to be a number.
   207  // If value is Nil, then 'defaultValue' will be returned.
   208  func (env *Env) IntDefault(idx int, defaultValue int) int {
   209  	if v := env.Get(idx); v.pType() == typ.Number {
   210  		return v.Int()
   211  	} else if v != Nil {
   212  		internal.Panic("expects argument #%d to be number or nil, got %v", idx+1, v.simple())
   213  	}
   214  	return defaultValue
   215  }
   216  
   217  // Float64 returns value at 'idx' in current stack and asserts its Type() to be a number.
   218  func (env *Env) Float64(idx int) float64 { return env.mustBe(typ.Number, idx).Float64() }
   219  
   220  // Object returns value at 'idx' in current stack and asserts its Type() to be an Object.
   221  func (env *Env) Object(idx int) *Object { return env.mustBe(typ.Object, idx).Object() }
   222  
   223  // Native returns value at 'idx' in current stack and asserts its Type() to be a Native.
   224  func (env *Env) Native(idx int) *Native { return env.mustBe(typ.Native, idx).Native() }
   225  
   226  // Interface returns value at 'idx' in current stack as interface{}
   227  func (env *Env) Interface(idx int) interface{} {
   228  	if idx == -1 {
   229  		return env.A.Interface()
   230  	}
   231  	return env.Get(idx).Interface()
   232  }
   233  
   234  func (env *Env) Shape(idx int, s string) Value {
   235  	v := env.Get(idx)
   236  	if err := TestShapeFast(v, s); err != nil {
   237  		internal.Panic("argument #%d: %v", idx, err)
   238  	}
   239  	return v
   240  }
   241  
   242  func (env *Env) This() Value { return env.A }
   243  
   244  func (env *Env) Self() *Object { return env.runtime.stack0.Callable }
   245  
   246  func (env *Env) Caller() *Object { return env.runtime.stack1.Callable }
   247  
   248  func (env *Env) mustBe(t typ.ValueType, idx int) (v Value) {
   249  	if idx == -1 {
   250  		v = env.A
   251  		if v.Type() != t {
   252  			internal.Panic("expects 'this' to be %v, got %v", t, v.simple())
   253  		}
   254  	} else {
   255  		v = env.Get(idx)
   256  		if v.Type() != t {
   257  			internal.Panic("expects argument #%d to be %v, got %v", idx+1, t, v.simple())
   258  		}
   259  	}
   260  	return v
   261  }
   262  
   263  func (env *Env) SetA(a Value) bool {
   264  	env.A = a
   265  	return true
   266  }
   267  
   268  func (env *Env) SetError(err error) bool {
   269  	env.A = Error(env, err)
   270  	return true
   271  }
   272  
   273  func (e *Env) MustProgram() *Program {
   274  	if e.top != nil {
   275  		return e.top
   276  	}
   277  	panic("out of program")
   278  }
   279  
   280  func (e *Env) Copy() *Env {
   281  	stk := e.CopyStack()
   282  	e2 := &Env{}
   283  	e2.A = e.A
   284  	e2.top = e.top
   285  	e2.stack = &stk
   286  	e2.runtime = e.runtime
   287  	e2.runtime.stackN = append([]Stacktrace{}, e2.runtime.stackN...)
   288  	return e2
   289  }
   290  
   291  func (e *Env) checkStackOverflow() {
   292  	if g := e.top; g != nil {
   293  		if int64(len(*g.stack)) > g.MaxStackSize {
   294  			panic("stack overflow")
   295  		}
   296  		if g.stopped {
   297  			panic("program stopped")
   298  		}
   299  	}
   300  }
   301  
   302  func (e *Env) Jump(label string) {
   303  	if label == "" {
   304  		return
   305  	}
   306  	pos, ok := e.Caller().fun.jumps[label]
   307  	if !ok {
   308  		internal.Panic("runtime jump: label %s not found", label)
   309  	}
   310  	e.runtime.stack1.Cursor = uint32(pos)
   311  }