github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/js/js.go (about) 1 // Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax. 2 // 3 // Use MakeWrapper to expose methods to JavaScript. Use MakeFullWrapper to expose methods AND fields to JavaScript. When passing values directly, the following type conversions are performed: 4 // 5 // | Go type | JavaScript type | Conversions back to interface{} | 6 // | --------------------- | --------------------- | ------------------------------- | 7 // | bool | Boolean | bool | 8 // | integers and floats | Number | float64 | 9 // | string | String | string | 10 // | []int8 | Int8Array | []int8 | 11 // | []int16 | Int16Array | []int16 | 12 // | []int32, []int | Int32Array | []int | 13 // | []uint8 | Uint8Array | []uint8 | 14 // | []uint16 | Uint16Array | []uint16 | 15 // | []uint32, []uint | Uint32Array | []uint | 16 // | []float32 | Float32Array | []float32 | 17 // | []float64 | Float64Array | []float64 | 18 // | all other slices | Array | []interface{} | 19 // | arrays | see slice type | see slice type | 20 // | functions | Function | func(...interface{}) *js.Object | 21 // | time.Time | Date | time.Time | 22 // | - | instanceof Node | *js.Object | 23 // | maps, structs | instanceof Object | map[string]interface{} | 24 // 25 // Additionally, for a struct containing a *js.Object field, only the content of the field will be passed to JavaScript and vice versa. 26 package js 27 28 // Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. A nil pointer to Object is equal to JavaScript's "null". Object can not be used as a map key. 29 type Object struct{ object *Object } 30 31 // Get returns the object's property with the given key. 32 func (o *Object) Get(key string) *Object { return o.object.Get(key) } 33 34 // Set assigns the value to the object's property with the given key. 35 func (o *Object) Set(key string, value interface{}) { o.object.Set(key, value) } 36 37 // Delete removes the object's property with the given key. 38 func (o *Object) Delete(key string) { o.object.Delete(key) } 39 40 // Length returns the object's "length" property, converted to int. 41 func (o *Object) Length() int { return o.object.Length() } 42 43 // Index returns the i'th element of an array. 44 func (o *Object) Index(i int) *Object { return o.object.Index(i) } 45 46 // SetIndex sets the i'th element of an array. 47 func (o *Object) SetIndex(i int, value interface{}) { o.object.SetIndex(i, value) } 48 49 // Call calls the object's method with the given name. 50 func (o *Object) Call(name string, args ...interface{}) *Object { return o.object.Call(name, args...) } 51 52 // Invoke calls the object itself. This will fail if it is not a function. 53 func (o *Object) Invoke(args ...interface{}) *Object { return o.object.Invoke(args...) } 54 55 // New creates a new instance of this type object. This will fail if it not a function (constructor). 56 func (o *Object) New(args ...interface{}) *Object { return o.object.New(args...) } 57 58 // Bool returns the object converted to bool according to JavaScript type conversions. 59 func (o *Object) Bool() bool { return o.object.Bool() } 60 61 // String returns the object converted to string according to JavaScript type conversions. 62 func (o *Object) String() string { return o.object.String() } 63 64 // Int returns the object converted to int according to JavaScript type conversions (parseInt). 65 func (o *Object) Int() int { return o.object.Int() } 66 67 // Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt). 68 func (o *Object) Int64() int64 { return o.object.Int64() } 69 70 // Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt). 71 func (o *Object) Uint64() uint64 { return o.object.Uint64() } 72 73 // Float returns the object converted to float64 according to JavaScript type conversions (parseFloat). 74 func (o *Object) Float() float64 { return o.object.Float() } 75 76 // Interface returns the object converted to interface{}. See table in package comment for details. 77 func (o *Object) Interface() interface{} { return o.object.Interface() } 78 79 // Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use. 80 func (o *Object) Unsafe() uintptr { return o.object.Unsafe() } 81 82 // Error encapsulates JavaScript errors. Those are turned into a Go panic and may be recovered, giving an *Error that holds the JavaScript error object. 83 type Error struct { 84 *Object 85 } 86 87 // Error returns the message of the encapsulated JavaScript error object. 88 func (err *Error) Error() string { 89 return "JavaScript error: " + err.Get("message").String() 90 } 91 92 // Stack returns the stack property of the encapsulated JavaScript error object. 93 func (err *Error) Stack() string { 94 return err.Get("stack").String() 95 } 96 97 // Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js). 98 var Global *Object 99 100 // Module gives the value of the "module" variable set by Node.js. Hint: Set a 101 // module export with 'js.Module.Get("exports").Set("exportName", ...)'. 102 // 103 // Note that js.Module is only defined in runtimes which support CommonJS 104 // modules (https://nodejs.org/api/modules.html). NodeJS supports it natively, 105 // but in browsers it can only be used if GopherJS output is passed through a 106 // bundler which implements CommonJS (for example, webpack or esbuild). 107 var Module *Object 108 109 // Undefined gives the JavaScript value "undefined". 110 var Undefined *Object 111 112 // Debugger gets compiled to JavaScript's "debugger;" statement. 113 func Debugger() {} 114 115 // InternalObject returns the internal JavaScript object that represents i. Not intended for public use. 116 func InternalObject(i interface{}) *Object { 117 return nil 118 } 119 120 // MakeFunc wraps a function and gives access to the values of JavaScript's "this" and "arguments" keywords. 121 func MakeFunc(fn func(this *Object, arguments []*Object) interface{}) *Object { 122 return Global.Call("$makeFunc", InternalObject(fn)) 123 } 124 125 // Keys returns the keys of the given JavaScript object. 126 func Keys(o *Object) []string { 127 if o == nil || o == Undefined { 128 return nil 129 } 130 a := Global.Get("Object").Call("keys", o) 131 s := make([]string, a.Length()) 132 for i := 0; i < a.Length(); i++ { 133 s[i] = a.Index(i).String() 134 } 135 return s 136 } 137 138 // MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. Use explicit getter and setter methods to expose struct fields to JavaScript. 139 func MakeWrapper(i interface{}) *Object { 140 v := InternalObject(i) 141 o := Global.Get("Object").New() 142 o.Set("__internal_object__", v) 143 methods := v.Get("constructor").Get("methods") 144 for i := 0; i < methods.Length(); i++ { 145 m := methods.Index(i) 146 if m.Get("pkg").String() != "" { // not exported 147 continue 148 } 149 o.Set(m.Get("name").String(), func(args ...*Object) *Object { 150 return Global.Call("$externalizeFunction", v.Get(m.Get("prop").String()), m.Get("typ"), true).Call("apply", v, args) 151 }) 152 } 153 return o 154 } 155 156 // MakeFullWrapper creates a JavaScript object which has wrappers for the exported 157 // methods of i, and, where i is a (pointer to a) struct value, wrapped getters 158 // and setters 159 // (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) 160 // for the non-embedded exported fields of i. Values accessed via these methods 161 // and getters are themselves wrapped when accessed, but an important point to 162 // note is that a new wrapped value is created on each access. 163 func MakeFullWrapper(i interface{}) *Object { 164 internalObj := InternalObject(i) 165 constructor := internalObj.Get("constructor") 166 167 wrapperObj := Global.Get("Object").New() 168 169 defineProperty := func(key string, descriptor M) { 170 Global.Get("Object").Call("defineProperty", wrapperObj, key, descriptor) 171 } 172 173 defineProperty("__internal_object__", M{ 174 "value": internalObj, 175 }) 176 177 { 178 // Calculate a sensible type string. 179 180 // We don't want to import any packages in this package, 181 // so we do some string operations by hand. 182 183 typ := constructor.Get("string").String() 184 pkg := constructor.Get("pkg").String() 185 186 ptr := "" 187 if typ[0] == '*' { 188 ptr = "*" 189 } 190 191 for i := 0; i < len(typ); i++ { 192 if typ[i] == '.' { 193 typ = typ[i+1:] 194 break 195 } 196 } 197 198 pkgTyp := pkg + "." + ptr + typ 199 defineProperty("$type", M{ 200 "value": pkgTyp, 201 }) 202 } 203 204 var fields *Object 205 methods := Global.Get("Array").New() 206 if ms := constructor.Get("methods"); ms != Undefined { 207 methods = methods.Call("concat", ms) 208 } 209 // If we are a pointer value then add fields from element, 210 // else the constructor itself will have them. 211 if e := constructor.Get("elem"); e != Undefined { 212 fields = e.Get("fields") 213 methods = methods.Call("concat", e.Get("methods")) 214 } else { 215 fields = constructor.Get("fields") 216 } 217 for i := 0; i < methods.Length(); i++ { 218 m := methods.Index(i) 219 if m.Get("pkg").String() != "" { // not exported 220 continue 221 } 222 defineProperty(m.Get("prop").String(), M{ 223 "value": func(args ...*Object) *Object { 224 return Global.Call("$externalizeFunction", internalObj.Get(m.Get("prop").String()), m.Get("typ"), true, InternalObject(MakeFullWrapper)).Call("apply", internalObj, args) 225 }, 226 }) 227 } 228 if fields != Undefined { 229 for i := 0; i < fields.Length(); i++ { 230 f := fields.Index(i) 231 if !f.Get("exported").Bool() { 232 continue 233 } 234 defineProperty(f.Get("prop").String(), M{ 235 "get": func() *Object { 236 vc := Global.Call("$copyIfRequired", internalObj.Get("$val").Get(f.Get("prop").String()), f.Get("typ")) 237 return Global.Call("$externalize", vc, f.Get("typ"), InternalObject(MakeFullWrapper)) 238 }, 239 "set": func(jv *Object) { 240 gv := Global.Call("$internalize", jv, f.Get("typ"), InternalObject(MakeFullWrapper)) 241 internalObj.Get("$val").Set(f.Get("prop").String(), gv) 242 }, 243 }) 244 } 245 } 246 return wrapperObj 247 } 248 249 // NewArrayBuffer creates a JavaScript ArrayBuffer from a byte slice. 250 func NewArrayBuffer(b []byte) *Object { 251 slice := InternalObject(b) 252 offset := slice.Get("$offset").Int() 253 length := slice.Get("$length").Int() 254 return slice.Get("$array").Get("buffer").Call("slice", offset, offset+length) 255 } 256 257 // M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion). 258 type M map[string]interface{} 259 260 // S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion). 261 type S []interface{} 262 263 func init() { 264 // Avoid dead code elimination. 265 e := Error{} 266 _ = e 267 }