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  }