github.com/vugu/vugu@v0.3.6-0.20240430171613-3f6f402e014b/js/impl-js.go (about)

     1  // +build js
     2  
     3  package js
     4  
     5  // IMPLEMENTATION NOTE: (7 Apr 2019, bgp)
     6  // Right now (as of Go 1.12), syscall/js essentially interacts with a big map of values
     7  // on the JS side of things in the browser.  "Value" is alias for "ref" which is basically
     8  // a map key with some bit swizzling to stuff it into a float64, presumably so it's both
     9  // easy to send back and forth between wasm and browser and also to encode info about the
    10  // type in there so things like Truthy() and Type(), etc. can be answered without
    11  // having to reach back out to the browser.  As such, Values and refs and everything else
    12  // don't appear to have any internal pointers or memory concerns at least on the Go side.
    13  // So the approach here is we just alias syscall/js.Value in each appropriate place and then
    14  // delegate all the calls using either casting or unsafe pointers to treat our js.Value exactly like
    15  // a syscall/js.Value.  (FuncOf and friends handled slightly differently.)
    16  // This works with the current implementation.  However if significant
    17  // changes are made to syscall/js (probable), it could very well break this package.
    18  // Since the future of Go-Wasm integration is unclear at this point, I'm just rolling
    19  // forward with the approach above and if 1.13 or later breaks things, I'll just have to
    20  // deal with it then.  If Wasm gains standardized support for GC and DOM references,
    21  // (see https://github.com/WebAssembly/proposals/issues/16) the odds are this package would
    22  // end up just being completely rewritten to use that API+instruction set.
    23  // It's also possible a WASI interface could address this functionality and/or make this entire package
    24  // go away, can't really tell; my crystal ball is broken.
    25  // See: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
    26  
    27  import sjs "syscall/js"
    28  
    29  // import "unsafe"
    30  
    31  // Error alias to syscall/js
    32  type Error struct {
    33  	// Value is the underlying JavaScript error value.
    34  	Value
    35  }
    36  
    37  // Error alias to syscall/js
    38  func (e Error) Error() string {
    39  	return sjs.Error{Value: sjs.Value(e.Value)}.Error()
    40  	// return (*(*sjs.Error)(unsafe.Pointer(&e))).Error()
    41  }
    42  
    43  // Func alias to syscall/js
    44  type Func struct {
    45  	Value
    46  	f sjs.Func // proxy for this func from syscall/js
    47  }
    48  
    49  // FuncOf alias to syscall/js
    50  func FuncOf(fn func(Value, []Value) interface{}) Func {
    51  
    52  	fn2 := func(this sjs.Value, args []sjs.Value) interface{} {
    53  		args2 := make([]Value, len(args))
    54  		for i := range args {
    55  			args2[i] = Value(args[i])
    56  		}
    57  		return fn(Value(this), args2)
    58  	}
    59  
    60  	f := sjs.FuncOf(fn2)
    61  
    62  	ret := Func{
    63  		Value: Value(f.Value),
    64  		f:     f,
    65  	}
    66  
    67  	return ret
    68  }
    69  
    70  // Release alias to syscall/js
    71  func (c Func) Release() {
    72  	// return (*(*sjs.Func)(unsafe.Pointer(&c))).Release()
    73  	c.f.Release()
    74  }
    75  
    76  // Type alias to syscall/js
    77  type Type int
    78  
    79  const (
    80  	TypeUndefined = Type(sjs.TypeUndefined)
    81  	TypeNull      = Type(sjs.TypeNull)
    82  	TypeBoolean   = Type(sjs.TypeBoolean)
    83  	TypeNumber    = Type(sjs.TypeNumber)
    84  	TypeString    = Type(sjs.TypeString)
    85  	TypeSymbol    = Type(sjs.TypeSymbol)
    86  	TypeObject    = Type(sjs.TypeObject)
    87  	TypeFunction  = Type(sjs.TypeFunction)
    88  )
    89  
    90  // String alias to syscall/js
    91  func (t Type) String() string {
    92  	return sjs.Type(t).String()
    93  }
    94  
    95  // Undefined alias to syscall/js
    96  func Undefined() Value {
    97  	return Value(sjs.Undefined())
    98  }
    99  
   100  // Null alias to syscall/js
   101  func Null() Value {
   102  	return Value(sjs.Null())
   103  }
   104  
   105  // Global alias to syscall/js
   106  func Global() Value {
   107  	return Value(sjs.Global())
   108  }
   109  
   110  // ValueOf alias to syscall/js
   111  func ValueOf(x interface{}) Value {
   112  	return Value(sjs.ValueOf(x))
   113  }
   114  
   115  // CopyBytesToGo alias to syscall/js
   116  func CopyBytesToGo(dst []byte, src Value) int {
   117  	return sjs.CopyBytesToGo(dst, sjs.Value(src))
   118  }
   119  
   120  // CopyBytesToJS alias to syscall/js
   121  func CopyBytesToJS(dst Value, src []byte) int {
   122  	return sjs.CopyBytesToJS(sjs.Value(dst), src)
   123  }
   124  
   125  // // TypedArray alias to syscall/js
   126  // type TypedArray struct {
   127  // 	Value
   128  // }
   129  
   130  // // TypedArrayOf alias to syscall/js
   131  // func TypedArrayOf(slice interface{}) TypedArray {
   132  // 	return TypedArray{Value: Value(sjs.TypedArrayOf(slice).Value)}
   133  // }
   134  
   135  // // Release alias to syscall/js
   136  // func (a TypedArray) Release() {
   137  // 	sjs.TypedArray{Value: sjs.Value(a.Value)}.Release()
   138  // }
   139  
   140  // ValueError alias to syscall/js
   141  type ValueError struct {
   142  	Method string
   143  	Type   Type
   144  }
   145  
   146  // Error alias to syscall/js
   147  func (e *ValueError) Error() string {
   148  	e2 := sjs.ValueError{Method: e.Method, Type: sjs.Type(e.Type)}
   149  	return e2.Error()
   150  }
   151  
   152  // Wrapper alias to syscall/js
   153  type Wrapper interface {
   154  	JSValue() Value
   155  }
   156  
   157  // Value alias to syscall/js
   158  type Value sjs.Value
   159  
   160  // JSValue alias to syscall/js
   161  func (v Value) JSValue() Value {
   162  	return v
   163  }
   164  
   165  // Type alias to syscall/js
   166  func (v Value) Type() Type {
   167  	return Type(sjs.Value(v).Type())
   168  }
   169  
   170  func (v Value) Get(p string) Value {
   171  	return Value(sjs.Value(v).Get(p))
   172  }
   173  
   174  func (v Value) Set(p string, x interface{}) {
   175  	sjs.Value(v).Set(p, x)
   176  }
   177  
   178  func (v Value) Index(i int) Value {
   179  	return Value(sjs.Value(v).Index(i))
   180  }
   181  
   182  func (v Value) SetIndex(i int, x interface{}) {
   183  	sjs.Value(v).SetIndex(i, x)
   184  }
   185  
   186  func (v Value) Length() int {
   187  	return sjs.Value(v).Length()
   188  }
   189  
   190  func (v Value) Call(m string, args ...interface{}) Value {
   191  	return Value(sjs.Value(v).Call(m, fixArgsToSjs(args)...))
   192  }
   193  
   194  func (v Value) Invoke(args ...interface{}) Value {
   195  	return Value(sjs.Value(v).Invoke(fixArgsToSjs(args)...))
   196  }
   197  
   198  func (v Value) New(args ...interface{}) Value {
   199  	return Value(sjs.Value(v).New(fixArgsToSjs(args)...))
   200  }
   201  
   202  func (v Value) Float() float64 {
   203  	return sjs.Value(v).Float()
   204  }
   205  
   206  func (v Value) Int() int {
   207  	return sjs.Value(v).Int()
   208  }
   209  
   210  func (v Value) Bool() bool {
   211  	return sjs.Value(v).Bool()
   212  }
   213  
   214  func (v Value) Truthy() bool {
   215  	return sjs.Value(v).Truthy()
   216  }
   217  
   218  func (v Value) String() string {
   219  	return sjs.Value(v).String()
   220  }
   221  
   222  func (v Value) InstanceOf(t Value) bool {
   223  	return sjs.Value(v).InstanceOf(sjs.Value(t))
   224  }
   225  
   226  func (v Value) IsUndefined() bool {
   227  	return sjs.Value(v).IsUndefined()
   228  }
   229  
   230  func (v Value) IsNull() bool {
   231  	return sjs.Value(v).IsNull()
   232  }
   233  
   234  func fixArgsToSjs(args []interface{}) []interface{} {
   235  	for i := 0; i < len(args); i++ {
   236  		v := args[i]
   237  		if val, ok := v.(Value); ok {
   238  			args[i] = sjs.Value(val) // convert to sjs.Value
   239  		}
   240  		if f, ok := v.(Func); ok {
   241  			args[i] = f.f
   242  		}
   243  		// if ta, ok := v.(TypedArray); ok {
   244  		// 	args[i] = sjs.TypedArray{Value: sjs.Value(ta.Value)}
   245  		// }
   246  	}
   247  	return args
   248  }