syscall/js: improve import functions

1. Make import functions not use the js.Value type directly,
but only the ref field. This gives more flexibility on the Go side
for the js.Value type, which is a preparation for adding
garbage collection of js.Value.

2. Turn import functions which are methods of js.Value into
package-level functions. This is necessary to make vet happy.

Change-Id: I69959bf1fbea0a0b99a552a1112ffcd0c024e9b8
Reviewed-on: https://go-review.googlesource.com/118656
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Richard Musiol 2018-06-13 15:43:54 +02:00 committed by Brad Fitzpatrick
parent f70d1e76cc
commit 2809b339b5
3 changed files with 132 additions and 99 deletions

View file

@ -173,48 +173,48 @@
crypto.getRandomValues(loadSlice(sp + 8));
},
// func boolVal(value bool) Value
// func boolVal(value bool) ref
"syscall/js.boolVal": (sp) => {
storeValue(sp + 16, mem().getUint8(sp + 8) !== 0);
},
// func intVal(value int) Value
// func intVal(value int) ref
"syscall/js.intVal": (sp) => {
storeValue(sp + 16, getInt64(sp + 8));
},
// func floatVal(value float64) Value
// func floatVal(value float64) ref
"syscall/js.floatVal": (sp) => {
storeValue(sp + 16, mem().getFloat64(sp + 8, true));
},
// func stringVal(value string) Value
// func stringVal(value string) ref
"syscall/js.stringVal": (sp) => {
storeValue(sp + 24, loadString(sp + 8));
},
// func (v Value) Get(key string) Value
"syscall/js.Value.Get": (sp) => {
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (sp) => {
storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16)));
},
// func (v Value) set(key string, value Value)
"syscall/js.Value.set": (sp) => {
// func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (sp) => {
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
},
// func (v Value) Index(i int) Value
"syscall/js.Value.Index": (sp) => {
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (sp) => {
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
},
// func (v Value) setIndex(i int, value Value)
"syscall/js.Value.setIndex": (sp) => {
// valueSetIndex(v ref, i int, x ref)
"syscall/js.valueSetIndex": (sp) => {
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
},
// func (v Value) call(name string, args []Value) (Value, bool)
"syscall/js.Value.call": (sp) => {
// func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (sp) => {
try {
const v = loadValue(sp + 8);
const m = Reflect.get(v, loadString(sp + 16));
@ -227,8 +227,8 @@
}
},
// func (v Value) invoke(args []Value) (Value, bool)
"syscall/js.Value.invoke": (sp) => {
// func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (sp) => {
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
@ -240,8 +240,8 @@
}
},
// func (v Value) new(args []Value) (Value, bool)
"syscall/js.Value.new": (sp) => {
// func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (sp) => {
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
@ -253,35 +253,35 @@
}
},
// func (v Value) Float() float64
"syscall/js.Value.Float": (sp) => {
// func valueFloat(v ref) float64
"syscall/js.valueFloat": (sp) => {
mem().setFloat64(sp + 16, parseFloat(loadValue(sp + 8)), true);
},
// func (v Value) Int() int
"syscall/js.Value.Int": (sp) => {
// func valueInt(v ref) int
"syscall/js.valueInt": (sp) => {
setInt64(sp + 16, parseInt(loadValue(sp + 8)));
},
// func (v Value) Bool() bool
"syscall/js.Value.Bool": (sp) => {
// func valueBool(v ref) bool
"syscall/js.valueBool": (sp) => {
mem().setUint8(sp + 16, !!loadValue(sp + 8));
},
// func (v Value) Length() int
"syscall/js.Value.Length": (sp) => {
// func valueLength(v ref) int
"syscall/js.valueLength": (sp) => {
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
},
// func (v Value) prepareString() (Value, int)
"syscall/js.Value.prepareString": (sp) => {
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (sp) => {
const str = encoder.encode(String(loadValue(sp + 8)));
storeValue(sp + 16, str);
setInt64(sp + 24, str.length);
},
// func (v Value) loadString(b []byte)
"syscall/js.Value.loadString": (sp) => {
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (sp) => {
const str = loadValue(sp + 8);
loadSlice(sp + 16).set(str);
},

View file

@ -11,11 +11,20 @@
// comprehensive API for users. It is exempt from the Go compatibility promise.
package js
import "unsafe"
import (
"unsafe"
)
// ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly itself.
type ref uint32
// Value represents a JavaScript value.
type Value struct {
ref uint32
ref ref
}
func makeValue(v ref) Value {
return Value{ref: v}
}
// Error wraps a JavaScript error.
@ -31,19 +40,19 @@ func (e Error) Error() string {
var (
// Undefined is the JavaScript value "undefined". The zero Value equals to Undefined.
Undefined = Value{0}
Undefined = makeValue(0)
// Null is the JavaScript value "null".
Null = Value{1}
Null = makeValue(1)
// Global is the JavaScript global object, usually "window" or "global".
Global = Value{2}
Global = makeValue(2)
// memory is the WebAssembly linear memory.
memory = Value{3}
memory = makeValue(3)
// resolveCallbackPromise is a function that the callback helper uses to resume the execution of Go's WebAssembly code.
resolveCallbackPromise = Value{4}
resolveCallbackPromise = makeValue(4)
)
var uint8Array = Global.Get("Uint8Array")
@ -58,37 +67,37 @@ func ValueOf(x interface{}) Value {
case nil:
return Null
case bool:
return boolVal(x)
return makeValue(boolVal(x))
case int:
return intVal(x)
return makeValue(intVal(x))
case int8:
return intVal(int(x))
return makeValue(intVal(int(x)))
case int16:
return intVal(int(x))
return makeValue(intVal(int(x)))
case int32:
return intVal(int(x))
return makeValue(intVal(int(x)))
case int64:
return intVal(int(x))
return makeValue(intVal(int(x)))
case uint:
return intVal(int(x))
return makeValue(intVal(int(x)))
case uint8:
return intVal(int(x))
return makeValue(intVal(int(x)))
case uint16:
return intVal(int(x))
return makeValue(intVal(int(x)))
case uint32:
return intVal(int(x))
return makeValue(intVal(int(x)))
case uint64:
return intVal(int(x))
return makeValue(intVal(int(x)))
case uintptr:
return intVal(int(x))
return makeValue(intVal(int(x)))
case unsafe.Pointer:
return intVal(int(uintptr(x)))
return makeValue(intVal(int(uintptr(x))))
case float32:
return floatVal(float64(x))
return makeValue(floatVal(float64(x)))
case float64:
return floatVal(x)
return makeValue(floatVal(x))
case string:
return stringVal(x)
return makeValue(stringVal(x))
case []byte:
if len(x) == 0 {
return uint8Array.New(memory.Get("buffer"), 0, 0)
@ -99,98 +108,122 @@ func ValueOf(x interface{}) Value {
}
}
func boolVal(x bool) Value
func boolVal(x bool) ref
func intVal(x int) Value
func intVal(x int) ref
func floatVal(x float64) Value
func floatVal(x float64) ref
func stringVal(x string) Value
func stringVal(x string) ref
// Get returns the JavaScript property p of value v.
func (v Value) Get(p string) Value
func (v Value) Get(p string) Value {
return makeValue(valueGet(v.ref, p))
}
func valueGet(v ref, p string) ref
// Set sets the JavaScript property p of value v to x.
func (v Value) Set(p string, x interface{}) {
v.set(p, ValueOf(x))
valueSet(v.ref, p, ValueOf(x).ref)
}
func (v Value) set(p string, x Value)
func valueSet(v ref, p string, x ref)
// Index returns JavaScript index i of value v.
func (v Value) Index(i int) Value
func (v Value) Index(i int) Value {
return makeValue(valueIndex(v.ref, i))
}
func valueIndex(v ref, i int) ref
// SetIndex sets the JavaScript index i of value v to x.
func (v Value) SetIndex(i int, x interface{}) {
v.setIndex(i, ValueOf(x))
valueSetIndex(v.ref, i, ValueOf(x).ref)
}
func (v Value) setIndex(i int, x Value)
func valueSetIndex(v ref, i int, x ref)
func makeArgs(args []interface{}) []Value {
argVals := make([]Value, len(args))
func makeArgs(args []interface{}) []ref {
argVals := make([]ref, len(args))
for i, arg := range args {
argVals[i] = ValueOf(arg)
argVals[i] = ValueOf(arg).ref
}
return argVals
}
// Length returns the JavaScript property "length" of v.
func (v Value) Length() int
func (v Value) Length() int {
return valueLength(v.ref)
}
func valueLength(v ref) int
// Call does a JavaScript call to the method m of value v with the given arguments.
// It panics if v has no method m.
func (v Value) Call(m string, args ...interface{}) Value {
res, ok := v.call(m, makeArgs(args))
res, ok := valueCall(v.ref, m, makeArgs(args))
if !ok {
panic(Error{res})
panic(Error{makeValue(res)})
}
return res
return makeValue(res)
}
func (v Value) call(m string, args []Value) (Value, bool)
func valueCall(v ref, m string, args []ref) (ref, bool)
// Invoke does a JavaScript call of the value v with the given arguments.
// It panics if v is not a function.
func (v Value) Invoke(args ...interface{}) Value {
res, ok := v.invoke(makeArgs(args))
res, ok := valueInvoke(v.ref, makeArgs(args))
if !ok {
panic(Error{res})
panic(Error{makeValue(res)})
}
return res
return makeValue(res)
}
func (v Value) invoke(args []Value) (Value, bool)
func valueInvoke(v ref, args []ref) (ref, bool)
// New uses JavaScript's "new" operator with value v as constructor and the given arguments.
// It panics if v is not a function.
func (v Value) New(args ...interface{}) Value {
res, ok := v.new(makeArgs(args))
res, ok := valueNew(v.ref, makeArgs(args))
if !ok {
panic(Error{res})
panic(Error{makeValue(res)})
}
return res
return makeValue(res)
}
func (v Value) new(args []Value) (Value, bool)
func valueNew(v ref, args []ref) (ref, bool)
// Float returns the value v converted to float64 according to JavaScript type conversions (parseFloat).
func (v Value) Float() float64
func (v Value) Float() float64 {
return valueFloat(v.ref)
}
func valueFloat(v ref) float64
// Int returns the value v converted to int according to JavaScript type conversions (parseInt).
func (v Value) Int() int
func (v Value) Int() int {
return valueInt(v.ref)
}
func valueInt(v ref) int
// Bool returns the value v converted to bool according to JavaScript type conversions.
func (v Value) Bool() bool
func (v Value) Bool() bool {
return valueBool(v.ref)
}
func valueBool(v ref) bool
// String returns the value v converted to string according to JavaScript type conversions.
func (v Value) String() string {
str, length := v.prepareString()
str, length := valuePrepareString(v.ref)
b := make([]byte, length)
str.loadString(b)
valueLoadString(str, b)
return string(b)
}
func (v Value) prepareString() (Value, int)
func valuePrepareString(v ref) (ref, int)
func (v Value) loadString(b []byte)
func valueLoadString(v ref, b []byte)

View file

@ -20,54 +20,54 @@ TEXT ·stringVal(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·Get(SB), NOSPLIT, $0
TEXT ·valueGet(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·set(SB), NOSPLIT, $0
TEXT ·valueSet(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·Index(SB), NOSPLIT, $0
TEXT ·valueIndex(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·setIndex(SB), NOSPLIT, $0
TEXT ·valueSetIndex(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·call(SB), NOSPLIT, $0
TEXT ·valueCall(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·invoke(SB), NOSPLIT, $0
TEXT ·valueInvoke(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·new(SB), NOSPLIT, $0
TEXT ·valueNew(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·Float(SB), NOSPLIT, $0
TEXT ·valueFloat(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·Int(SB), NOSPLIT, $0
TEXT ·valueInt(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·Bool(SB), NOSPLIT, $0
TEXT ·valueBool(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·Length(SB), NOSPLIT, $0
TEXT ·valueLength(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·prepareString(SB), NOSPLIT, $0
TEXT ·valuePrepareString(SB), NOSPLIT, $0
CallImport
RET
TEXT ·Value·loadString(SB), NOSPLIT, $0
TEXT ·valueLoadString(SB), NOSPLIT, $0
CallImport
RET