...

Source file src/syscall/js/func.go

     1	// Copyright 2018 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// +build js,wasm
     6	
     7	package js
     8	
     9	import "sync"
    10	
    11	var (
    12		funcsMu    sync.Mutex
    13		funcs             = make(map[uint32]func(Value, []Value) interface{})
    14		nextFuncID uint32 = 1
    15	)
    16	
    17	var _ Wrapper = Func{} // Func must implement Wrapper
    18	
    19	// Func is a wrapped Go function to be called by JavaScript.
    20	type Func struct {
    21		Value // the JavaScript function that invokes the Go function
    22		id    uint32
    23	}
    24	
    25	// FuncOf returns a wrapped function.
    26	//
    27	// Invoking the JavaScript function will synchronously call the Go function fn with the value of JavaScript's
    28	// "this" keyword and the arguments of the invocation.
    29	// The return value of the invocation is the result of the Go function mapped back to JavaScript according to ValueOf.
    30	//
    31	// A wrapped function triggered during a call from Go to JavaScript gets executed on the same goroutine.
    32	// A wrapped function triggered by JavaScript's event loop gets executed on an extra goroutine.
    33	// Blocking operations in the wrapped function will block the event loop.
    34	// As a consequence, if one wrapped function blocks, other wrapped funcs will not be processed.
    35	// A blocking function should therefore explicitly start a new goroutine.
    36	//
    37	// Func.Release must be called to free up resources when the function will not be used any more.
    38	func FuncOf(fn func(this Value, args []Value) interface{}) Func {
    39		funcsMu.Lock()
    40		id := nextFuncID
    41		nextFuncID++
    42		funcs[id] = fn
    43		funcsMu.Unlock()
    44		return Func{
    45			id:    id,
    46			Value: jsGo.Call("_makeFuncWrapper", id),
    47		}
    48	}
    49	
    50	// Release frees up resources allocated for the function.
    51	// The function must not be invoked after calling Release.
    52	func (c Func) Release() {
    53		funcsMu.Lock()
    54		delete(funcs, c.id)
    55		funcsMu.Unlock()
    56	}
    57	
    58	// setEventHandler is defined in the runtime package.
    59	func setEventHandler(fn func())
    60	
    61	func init() {
    62		setEventHandler(handleEvent)
    63	}
    64	
    65	func handleEvent() {
    66		cb := jsGo.Get("_pendingEvent")
    67		if cb == Null() {
    68			return
    69		}
    70		jsGo.Set("_pendingEvent", Null())
    71	
    72		id := uint32(cb.Get("id").Int())
    73		if id == 0 { // zero indicates deadlock
    74			select {}
    75		}
    76		funcsMu.Lock()
    77		f, ok := funcs[id]
    78		funcsMu.Unlock()
    79		if !ok {
    80			Global().Get("console").Call("error", "call to released function")
    81			return
    82		}
    83	
    84		this := cb.Get("this")
    85		argsObj := cb.Get("args")
    86		args := make([]Value, argsObj.Length())
    87		for i := range args {
    88			args[i] = argsObj.Index(i)
    89		}
    90		result := f(this, args)
    91		cb.Set("result", result)
    92	}
    93	

View as plain text