github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/compiler/natives/src/syscall/js/js.go (about)

     1  // +build js
     2  
     3  package js
     4  
     5  import (
     6  	"reflect"
     7  	"unsafe"
     8  
     9  	"github.com/gopherjs/gopherjs/js"
    10  )
    11  
    12  type Type int
    13  
    14  const (
    15  	TypeUndefined Type = iota
    16  	TypeNull
    17  	TypeBoolean
    18  	TypeNumber
    19  	TypeString
    20  	TypeSymbol
    21  	TypeObject
    22  	TypeFunction
    23  )
    24  
    25  func (t Type) String() string {
    26  	switch t {
    27  	case TypeUndefined:
    28  		return "undefined"
    29  	case TypeNull:
    30  		return "null"
    31  	case TypeBoolean:
    32  		return "boolean"
    33  	case TypeNumber:
    34  		return "number"
    35  	case TypeString:
    36  		return "string"
    37  	case TypeSymbol:
    38  		return "symbol"
    39  	case TypeObject:
    40  		return "object"
    41  	case TypeFunction:
    42  		return "function"
    43  	default:
    44  		panic("bad type")
    45  	}
    46  }
    47  
    48  func Global() Value {
    49  	return objectToValue(js.Global)
    50  }
    51  
    52  func Null() Value {
    53  	return objectToValue(nil)
    54  }
    55  
    56  func Undefined() Value {
    57  	return objectToValue(js.Undefined)
    58  }
    59  
    60  type Func struct {
    61  	Value
    62  }
    63  
    64  func (f Func) Release() {
    65  	f.Value = Null()
    66  }
    67  
    68  func FuncOf(fn func(this Value, args []Value) interface{}) Func {
    69  	return Func{
    70  		Value: objectToValue(js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} {
    71  			vargs := make([]Value, len(args))
    72  			for i, a := range args {
    73  				vargs[i] = objectToValue(a)
    74  			}
    75  			return fn(objectToValue(this), vargs)
    76  		})),
    77  	}
    78  }
    79  
    80  type Error struct {
    81  	Value
    82  }
    83  
    84  func (e Error) Error() string {
    85  	return "JavaScript error: " + e.Get("message").String()
    86  }
    87  
    88  type Value struct {
    89  	v *js.Object
    90  
    91  	// inited represents whether Value is non-zero value. true represents the value is not 'undefined'.
    92  	inited bool
    93  }
    94  
    95  func objectToValue(obj *js.Object) Value {
    96  	if obj == js.Undefined {
    97  		return Value{}
    98  	}
    99  	return Value{obj, true}
   100  }
   101  
   102  var (
   103  	id           *js.Object
   104  	instanceOf   *js.Object
   105  	getValueType *js.Object
   106  )
   107  
   108  func init() {
   109  	if js.Global != nil {
   110  		id = js.Global.Call("eval", "(function(x) { return x; })")
   111  		instanceOf = js.Global.Call("eval", "(function(x, y) { return x instanceof y; })")
   112  		getValueType = js.Global.Call("eval", `(function(x) {
   113    if (typeof(x) === "undefined") {
   114      return 0; // TypeUndefined
   115    }
   116    if (x === null) {
   117      return 1; // TypeNull
   118    }
   119    if (typeof(x) === "boolean") {
   120      return 2; // TypeBoolean
   121    }
   122    if (typeof(x) === "number") {
   123      return 3; // TypeNumber
   124    }
   125    if (typeof(x) === "string") {
   126      return 4; // TypeString
   127    }
   128    if (typeof(x) === "symbol") {
   129      return 5; // TypeSymbol
   130    }
   131    if (typeof(x) === "function") {
   132      return 7; // TypeFunction
   133    }
   134    return 6; // TypeObject
   135  })`)
   136  	}
   137  }
   138  
   139  func ValueOf(x interface{}) Value {
   140  	switch x := x.(type) {
   141  	case Value:
   142  		return x
   143  	case Func:
   144  		return x.Value
   145  	case TypedArray:
   146  		return x.Value
   147  	case nil:
   148  		return Null()
   149  	case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, unsafe.Pointer, string, map[string]interface{}, []interface{}:
   150  		return objectToValue(id.Invoke(x))
   151  	default:
   152  		panic(`invalid arg: ` + reflect.TypeOf(x).String())
   153  	}
   154  }
   155  
   156  func (v Value) internal() *js.Object {
   157  	if !v.inited {
   158  		return js.Undefined
   159  	}
   160  	return v.v
   161  }
   162  
   163  func (v Value) Bool() bool {
   164  	if vType := v.Type(); vType != TypeBoolean {
   165  		panic(&ValueError{"Value.Bool", vType})
   166  	}
   167  	return v.internal().Bool()
   168  }
   169  
   170  // convertArgs converts arguments into values for GopherJS arguments.
   171  func convertArgs(args ...interface{}) []interface{} {
   172  	newArgs := []interface{}{}
   173  	for _, arg := range args {
   174  		v := ValueOf(arg)
   175  		newArgs = append(newArgs, v.internal())
   176  	}
   177  	return newArgs
   178  }
   179  
   180  func (v Value) Call(m string, args ...interface{}) Value {
   181  	if vType := v.Type(); vType != TypeObject && vType != TypeFunction {
   182  		panic(&ValueError{"Value.Call", vType})
   183  	}
   184  	if propType := v.Get(m).Type(); propType != TypeFunction {
   185  		panic("js: Value.Call: property " + m + " is not a function, got " + propType.String())
   186  	}
   187  	return objectToValue(v.internal().Call(m, convertArgs(args...)...))
   188  }
   189  
   190  func (v Value) Float() float64 {
   191  	if vType := v.Type(); vType != TypeNumber {
   192  		panic(&ValueError{"Value.Float", vType})
   193  	}
   194  	return v.internal().Float()
   195  }
   196  
   197  func (t Type) isObject() bool {
   198  	return t == TypeObject || t == TypeFunction
   199  }
   200  
   201  func (v Value) Get(p string) Value {
   202  	if vType := v.Type(); !vType.isObject() {
   203  		panic(&ValueError{"Value.Get", vType})
   204  	}
   205  	return objectToValue(v.internal().Get(p))
   206  }
   207  
   208  func (v Value) Index(i int) Value {
   209  	if vType := v.Type(); !vType.isObject() {
   210  		panic(&ValueError{"Value.Index", vType})
   211  	}
   212  	return objectToValue(v.internal().Index(i))
   213  }
   214  
   215  func (v Value) Int() int {
   216  	if vType := v.Type(); vType != TypeNumber {
   217  		panic(&ValueError{"Value.Int", vType})
   218  	}
   219  	return v.internal().Int()
   220  }
   221  
   222  func (v Value) InstanceOf(t Value) bool {
   223  	return instanceOf.Invoke(v.internal(), t.internal()).Bool()
   224  }
   225  
   226  func (v Value) Invoke(args ...interface{}) Value {
   227  	if vType := v.Type(); vType != TypeFunction {
   228  		panic(&ValueError{"Value.Invoke", vType})
   229  	}
   230  	return objectToValue(v.internal().Invoke(convertArgs(args...)...))
   231  }
   232  
   233  func (v Value) JSValue() Value {
   234  	return v
   235  }
   236  
   237  func (v Value) Length() int {
   238  	return v.internal().Length()
   239  }
   240  
   241  func (v Value) newWrap(args ...interface{}) (obj *js.Object, err error) {
   242  	defer func() {
   243  		ret := recover()
   244  		if r, ok := ret.(*js.Error); ok {
   245  			err = r
   246  		}
   247  	}()
   248  	obj = v.internal().New(convertArgs(args...)...)
   249  	return
   250  }
   251  
   252  func (v Value) New(args ...interface{}) Value {
   253  	obj, err := v.newWrap(args...)
   254  	if err != nil {
   255  		if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
   256  			panic(&ValueError{"Value.Invoke", vType})
   257  		}
   258  		panic(Error{objectToValue(obj)})
   259  	}
   260  	return objectToValue(obj)
   261  }
   262  
   263  func (v Value) Set(p string, x interface{}) {
   264  	if vType := v.Type(); !vType.isObject() {
   265  		panic(&ValueError{"Value.Set", vType})
   266  	}
   267  	v.internal().Set(p, convertArgs(x)[0])
   268  }
   269  
   270  func (v Value) SetIndex(i int, x interface{}) {
   271  	if vType := v.Type(); !vType.isObject() {
   272  		panic(&ValueError{"Value.SetIndex", vType})
   273  	}
   274  	v.internal().SetIndex(i, convertArgs(x)[0])
   275  }
   276  
   277  // func (v Value) String() string {
   278  // 	return v.internal().String()
   279  // }
   280  
   281  func (v Value) Truthy() bool {
   282  	return v.internal().Bool()
   283  }
   284  
   285  func (v Value) Type() Type {
   286  	return Type(getValueType.Invoke(v.internal()).Int())
   287  }
   288  
   289  type TypedArray struct {
   290  	Value
   291  }
   292  
   293  func TypedArrayOf(slice interface{}) TypedArray {
   294  	switch slice := slice.(type) {
   295  	case []int8, []int16, []int32, []uint8, []uint16, []uint32, []float32, []float64:
   296  		return TypedArray{objectToValue(id.Invoke(slice))}
   297  	default:
   298  		panic("TypedArrayOf: not a supported slice")
   299  	}
   300  }
   301  
   302  func (t *TypedArray) Release() {
   303  	t.Value = Value{}
   304  }
   305  
   306  type ValueError struct {
   307  	Method string
   308  	Type   Type
   309  }
   310  
   311  func (e *ValueError) Error() string {
   312  	return "syscall/js: call of " + e.Method + " on " + e.Type.String()
   313  }
   314  
   315  type Wrapper interface {
   316  	JSValue() Value
   317  }