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

     1  package bas
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"reflect"
     9  	"runtime"
    10  	"runtime/debug"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  
    15  	"github.com/coyove/nj/internal"
    16  	"github.com/coyove/nj/typ"
    17  )
    18  
    19  var (
    20  	ioWriterType = reflect.TypeOf((*io.Writer)(nil)).Elem()
    21  	ioReaderType = reflect.TypeOf((*io.Reader)(nil)).Elem()
    22  	ioCloserType = reflect.TypeOf((*io.Closer)(nil)).Elem()
    23  	errType      = reflect.TypeOf((*error)(nil)).Elem()
    24  	valueType    = reflect.TypeOf(Value{})
    25  )
    26  
    27  func (v Value) Error() *ExecError {
    28  	return v.Native().Unwrap().(*ExecError)
    29  }
    30  
    31  func (v Value) Bytes() []byte {
    32  	return v.Native().Unwrap().([]byte)
    33  }
    34  
    35  func Write(w io.Writer, v Value) (int, error) {
    36  	switch v.Type() {
    37  	case typ.Nil:
    38  		return 0, nil
    39  	case typ.Native:
    40  		if v.IsBytes() {
    41  			return w.Write(v.Bytes())
    42  		}
    43  	case typ.String:
    44  		return internal.WriteString(w, v.Str())
    45  	}
    46  	v.Stringify(w, typ.MarshalToString)
    47  	return 1, nil
    48  }
    49  
    50  func (v Value) IsBytes() bool {
    51  	return v.Type() == typ.Native && v.Native().meta.Proto.HasPrototype(&Proto.Bytes)
    52  }
    53  
    54  func (v Value) IsError() bool {
    55  	return v.Type() == typ.Native && v.Native().meta.Proto.HasPrototype(&Proto.Error)
    56  }
    57  
    58  func (v Value) IsPanic() bool {
    59  	return v.Type() == typ.Native && v.Native().meta.Proto.HasPrototype(&Proto.Panic)
    60  }
    61  
    62  func DeepEqual(a, b Value) bool {
    63  	if a.Equal(b) {
    64  		return true
    65  	}
    66  	if at, bt := a.Type(), b.Type(); at == bt {
    67  		switch at {
    68  		case typ.Native:
    69  			if a.IsArray() && b.IsArray() {
    70  				flag := a.Native().Len() == b.Native().Len()
    71  				if !flag {
    72  					return false
    73  				}
    74  				for i := 0; flag && i < a.Native().Len(); i++ {
    75  					flag = DeepEqual(b.Native().Get(i), a.Native().Get(i))
    76  				}
    77  				return flag
    78  			}
    79  		case typ.Object:
    80  			flag := a.Object().Len() == b.Object().Len()
    81  			if !flag {
    82  				return false
    83  			}
    84  			a.Object().Foreach(func(k Value, v *Value) bool {
    85  				flag = DeepEqual(b.Object().Get(k), *v)
    86  				return flag
    87  			})
    88  			return flag
    89  		}
    90  	}
    91  	return false
    92  }
    93  
    94  func lessStr(a, b Value) bool {
    95  	if a.isSmallString() && b.isSmallString() {
    96  		al := (a.unsafeAddr() - uintptr(smallStrMarker)) / 8 * 8
    97  		bl := (b.unsafeAddr() - uintptr(smallStrMarker)) / 8 * 8
    98  		av := a.v >> (64 - al)
    99  		bv := b.v >> (64 - bl)
   100  		return av < bv
   101  	}
   102  	return a.Str() < b.Str()
   103  }
   104  
   105  func (a Value) Less(b Value) bool {
   106  	at, bt := a.Type(), b.Type()
   107  	if at != bt {
   108  		return at < bt
   109  	}
   110  	switch at {
   111  	case typ.Number:
   112  		if a.IsInt64() && b.IsInt64() {
   113  			return a.UnsafeInt64() < b.UnsafeInt64()
   114  		}
   115  		return a.Float64() < b.Float64()
   116  	case typ.String:
   117  		return lessStr(a, b)
   118  	}
   119  	return a.unsafeAddr() < b.unsafeAddr()
   120  }
   121  
   122  func (a Value) HasPrototype(p *Object) bool {
   123  	switch a.Type() {
   124  	case typ.Nil:
   125  		return p == nil
   126  	case typ.Object:
   127  		return a.Object().HasPrototype(p)
   128  	case typ.Bool:
   129  		return p == &Proto.Bool
   130  	case typ.Number:
   131  		return p == &Proto.Float || (a.IsInt64() && p == &Proto.Int)
   132  	case typ.String:
   133  		return p == &Proto.Str
   134  	case typ.Native:
   135  		return a.Native().HasPrototype(p)
   136  	}
   137  	return false
   138  }
   139  
   140  // ToType converts value to reflect.Value based on reflect.Type.
   141  // The result, even not being Zero, may be illegal to use in certain calls.
   142  func (v Value) ToType(t reflect.Type) reflect.Value {
   143  	if t == valueType {
   144  		return reflect.ValueOf(v)
   145  	}
   146  	if t == nil {
   147  		return reflect.ValueOf(v.Interface())
   148  	}
   149  
   150  	switch vt := v.Type(); vt {
   151  	case typ.Nil:
   152  		if t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
   153  			return reflect.Zero(t)
   154  		}
   155  	case typ.Object:
   156  		if t.Kind() == reflect.Func {
   157  			return reflect.MakeFunc(t, func(args []reflect.Value) (results []reflect.Value) {
   158  				var a []Value
   159  				for i := range args {
   160  					if i == len(args)-1 && t.IsVariadic() {
   161  						// TODO: performance
   162  						for j := 0; j < args[i].Len(); j++ {
   163  							a = append(a, ValueOf(args[i].Index(j).Interface()))
   164  						}
   165  					} else {
   166  						a = append(a, ValueOf(args[i].Interface()))
   167  					}
   168  				}
   169  				out := v.Object().Call(nil, a...)
   170  				if to := t.NumOut(); to == 1 {
   171  					results = []reflect.Value{out.ToType(t.Out(0))}
   172  				} else if to > 1 {
   173  					if !out.IsArray() {
   174  						internal.Panic("ToType: function should return %d values (sig: %v)", to, t)
   175  					}
   176  					results = make([]reflect.Value, t.NumOut())
   177  					for i := range results {
   178  						results[i] = out.Native().Get(i).ToType(t.Out(i))
   179  					}
   180  				}
   181  				return
   182  			})
   183  		}
   184  		if t.Kind() == reflect.Map {
   185  			s := reflect.MakeMap(t)
   186  			kt, vt := t.Key(), t.Elem()
   187  			v.Object().Foreach(func(k Value, v *Value) bool {
   188  				s.SetMapIndex(k.ToType(kt), v.ToType(vt))
   189  				return true
   190  			})
   191  			return s
   192  		}
   193  		if t.Implements(ioWriterType) || t.Implements(ioReaderType) || t.Implements(ioCloserType) {
   194  			return reflect.ValueOf(valueIO(v))
   195  		}
   196  	case typ.Native:
   197  		a := v.Native().Unwrap()
   198  		if t.Implements(ioWriterType) || t.Implements(ioReaderType) || t.Implements(ioCloserType) {
   199  			return reflect.ValueOf(a)
   200  		}
   201  		if t.Implements(errType) {
   202  			return reflect.ValueOf(v.Error())
   203  		}
   204  		if t == reflect.TypeOf(a) {
   205  			return reflect.ValueOf(a)
   206  		}
   207  		if v.IsArray() {
   208  			switch a := v.Native(); t.Kind() {
   209  			case reflect.Slice:
   210  				s := reflect.MakeSlice(t, a.Len(), a.Len())
   211  				for i := 0; i < a.Len(); i++ {
   212  					s.Index(i).Set(a.Get(i).ToType(t.Elem()))
   213  				}
   214  				return s
   215  			case reflect.Array:
   216  				s := reflect.New(t).Elem()
   217  				for i := 0; i < a.Len(); i++ {
   218  					s.Index(i).Set(a.Get(i).ToType(t.Elem()))
   219  				}
   220  				return s
   221  			}
   222  		}
   223  	case typ.Number:
   224  		if t.Kind() >= reflect.Int && t.Kind() <= reflect.Float64 {
   225  			return reflect.ValueOf(v.Interface()).Convert(t)
   226  		}
   227  	case typ.Bool:
   228  		if t.Kind() == reflect.Bool {
   229  			return reflect.ValueOf(v.Bool())
   230  		}
   231  	case typ.String:
   232  		if t.Kind() == reflect.String {
   233  			return reflect.ValueOf(v.Str())
   234  		}
   235  	}
   236  	if t.Kind() == reflect.Interface {
   237  		return reflect.ValueOf(v.Interface())
   238  	}
   239  	panic("ToType: failed to convert " + v.simple() + " to " + t.String())
   240  }
   241  
   242  func (v Value) Len() int {
   243  	switch v.Type() {
   244  	case typ.String:
   245  		if v.isSmallString() {
   246  			return int(uintptr(v.p)-uintptr(smallStrMarker)) / 8
   247  		}
   248  		return len(*(*string)(v.p))
   249  	case typ.Native:
   250  		return v.Native().Len()
   251  	case typ.Object:
   252  		return v.Object().Len()
   253  	case typ.Nil:
   254  		return 0
   255  	}
   256  	panic("can't measure length of " + v.simple())
   257  }
   258  
   259  func setObjectRecv(v, r Value) Value {
   260  	if v.IsObject() {
   261  		o := v.Object().Copy()
   262  		o.this = r
   263  		v = o.ToValue()
   264  	}
   265  	return v
   266  }
   267  
   268  func (v Value) simple() string {
   269  	switch vt := v.Type(); vt {
   270  	case typ.Object:
   271  		if f := v.Object().fun; f != nil && f != objDefaultFun {
   272  			return v.Object().funcSig()
   273  		}
   274  		return fmt.Sprintf("%s{%d}", v.Object().Name(), v.Object().Len())
   275  	case typ.Native:
   276  		a := v.Native()
   277  		if v.IsArray() {
   278  			return fmt.Sprintf("%s[%d]", a.meta.Name, a.Len())
   279  		}
   280  		return a.meta.Name
   281  	case typ.Number, typ.Bool:
   282  		return v.String()
   283  	case typ.String:
   284  		ln := (v).Len()
   285  		if ln < 16 {
   286  			return strconv.Quote(v.Str())
   287  		}
   288  		return fmt.Sprintf("string(%db)", ln)
   289  	default:
   290  		return v.Type().String()
   291  	}
   292  }
   293  
   294  func multiMap(e *Env, fun *Object, t Value, n int) Value {
   295  	if n < 1 || n > runtime.NumCPU()*1e3 {
   296  		internal.Panic("invalid number of goroutines: %v", n)
   297  	}
   298  
   299  	type payload struct {
   300  		i int
   301  		k Value
   302  		v *Value
   303  	}
   304  
   305  	var outError Value
   306  	work := func(e *Env, fun *Object, p payload) {
   307  		defer catchPanicError(e, &outError)
   308  		if p.i == -1 {
   309  			*p.v = fun.Call(e, p.k, *p.v)
   310  		} else {
   311  			t.Native().Set(p.i, fun.Call(e, Int(p.i), p.k))
   312  		}
   313  	}
   314  
   315  	if n == 1 {
   316  		if t.IsArray() {
   317  			for i := 0; outError == Nil && i < t.Native().Len(); i++ {
   318  				work(e, fun, payload{i, t.Native().Get(i), nil})
   319  			}
   320  		} else {
   321  			t.Object().Foreach(func(k Value, v *Value) bool {
   322  				work(e, fun, payload{-1, k, v})
   323  				return outError == Nil
   324  			})
   325  		}
   326  	} else {
   327  		var in = make(chan payload, t.Len())
   328  		var wg sync.WaitGroup
   329  		wg.Add(n)
   330  		for i := 0; i < n; i++ {
   331  			go func(e *Env) {
   332  				defer wg.Done()
   333  				for p := range in {
   334  					if outError != Nil {
   335  						break
   336  					}
   337  					work(e, fun, p)
   338  				}
   339  			}(e.Copy())
   340  		}
   341  
   342  		if t.IsArray() {
   343  			for i := 0; i < t.Native().Len(); i++ {
   344  				in <- payload{i, t.Native().Get(i), nil}
   345  			}
   346  		} else {
   347  			t.Object().Foreach(func(k Value, v *Value) bool {
   348  				in <- payload{-1, k, v}
   349  				return true
   350  			})
   351  		}
   352  		close(in)
   353  
   354  		wg.Wait()
   355  	}
   356  	if outError != Nil {
   357  		return outError
   358  	}
   359  	return t
   360  }
   361  
   362  func Fprintf(w io.Writer, format string, args ...Value) {
   363  	tmp := bytes.Buffer{}
   364  	ai := 0
   365  NEXT:
   366  	for len(format) > 0 {
   367  		idx := strings.Index(format, "%")
   368  		if idx == -1 {
   369  			internal.WriteString(w, format)
   370  			break
   371  		}
   372  		if idx == len(format)-1 {
   373  			internal.WriteString(w, "%?(NOVERB)")
   374  			break
   375  		}
   376  		internal.WriteString(w, format[:idx])
   377  		if format[idx+1] == '%' {
   378  			internal.WriteString(w, "%")
   379  			format = format[idx+2:]
   380  			continue
   381  		}
   382  
   383  		tmp.Reset()
   384  		tmp.WriteByte('%')
   385  		format = format[idx+1:]
   386  
   387  		preferNumber := ' '
   388  		for found := false; len(format) > 0 && !found; {
   389  			head := format[0]
   390  			tmp.WriteByte(head)
   391  			format = format[1:]
   392  			switch head {
   393  			case 'b', 'd', 'o', 'O', 'c', 'U':
   394  				preferNumber = 'i'
   395  				found = true
   396  			case 'f', 'F', 'g', 'G', 'e', 'E':
   397  				preferNumber = 'f'
   398  				found = true
   399  			case 's', 'q', 'x', 'X', 'v', 't', 'p':
   400  				found = true
   401  			case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '-', '+', '#', ' ':
   402  			default:
   403  				internal.WriteString(w, tmp.String()+"(BAD)")
   404  				goto NEXT
   405  			}
   406  		}
   407  		if ai >= len(args) {
   408  			internal.WriteString(w, tmp.String()+"(MISSING)")
   409  		} else {
   410  			v := args[ai].Interface()
   411  			if a := args[ai]; a.IsNumber() {
   412  				if preferNumber == 'i' {
   413  					v = a.Int64()
   414  				} else if preferNumber == 'f' {
   415  					v = a.Float64()
   416  				} else if a.IsInt64() {
   417  					v = a.Int64()
   418  				} else {
   419  					v = a.Float64()
   420  				}
   421  			}
   422  			fmt.Fprintf(w, tmp.String(), v)
   423  		}
   424  		ai++
   425  	}
   426  }
   427  
   428  func Fprint(w io.Writer, values ...Value) {
   429  	for _, v := range values {
   430  		v.Stringify(w, typ.MarshalToString)
   431  	}
   432  }
   433  
   434  func Fprintln(w io.Writer, values ...Value) {
   435  	for _, v := range values {
   436  		v.Stringify(w, typ.MarshalToString)
   437  		internal.WriteString(w, " ")
   438  	}
   439  	internal.WriteString(w, "\n")
   440  }
   441  
   442  func bassertTwoInts(op string, va, vb *Value) {
   443  	if !va.IsInt64() || !vb.IsInt64() {
   444  		internal.Panic("bitwise "+op+" requires integer numbers, got %v and %v", va.simple(), vb.simple())
   445  	}
   446  }
   447  
   448  func bassertOneInt(op string, va Value) {
   449  	if !va.IsInt64() {
   450  		internal.Panic("bitwise "+op+" requires integer numbers, got %v", va.simple())
   451  	}
   452  }
   453  
   454  func panicNotEnoughArgs(a *Object) {
   455  	panic("not enough arguments to call " + a.Name())
   456  }
   457  
   458  func catchPanicError(e *Env, err *Value) {
   459  	if r := recover(); r != nil {
   460  		if internal.IsDebug() {
   461  			log.Println(string(debug.Stack()))
   462  		}
   463  		rv, ok := r.(Value)
   464  		if ok {
   465  			r = rv.Interface()
   466  		}
   467  		er, _ := r.(*ExecError)
   468  		if er != nil {
   469  			er.fatal = true
   470  			*err = NewNativeWithMeta(er, &Proto.PanicMeta).ToValue()
   471  		} else {
   472  			er, _ := r.(error)
   473  			if er == nil {
   474  				er = fmt.Errorf("%v", r)
   475  			}
   476  
   477  			ee := &ExecError{
   478  				root:   er,
   479  				stacks: e.runtime.Stacktrace(true),
   480  				fatal:  true,
   481  			}
   482  			*err = NewNativeWithMeta(ee, &Proto.PanicMeta).ToValue()
   483  		}
   484  	}
   485  }