From 4b066d7aa8f0abc013a751ba4165322be4abe575 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 11 Jan 2024 13:50:14 -0700 Subject: [PATCH 1/7] syscall/js: Add methods (js.Value).GetSymbol, SetSymbol, and DeleteSymbol Support set symbols on an object. For #62339. --- misc/wasm/wasm_exec.js | 20 ++++++++++++++ src/syscall/js/js.go | 57 ++++++++++++++++++++++++++++++++++++++- src/syscall/js/js_test.go | 52 ++++++++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js index bc6f210242824c..c5387df896f187 100644 --- a/misc/wasm/wasm_exec.js +++ b/misc/wasm/wasm_exec.js @@ -334,6 +334,26 @@ Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); }, + // func valueGetRef(v ref, s ref) ref + "syscall/js.valueGetRef": (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadValue(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 24, result); + }, + + // func valueSetRef(v ref, s ref, x ref) + "syscall/js.valueSetRef": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadValue(sp + 16), loadValue(sp + 24)); + }, + + // func valueDeleteRef(v ref, s ref) + "syscall/js.valueDeleteRef": (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadValue(sp + 16)); + }, + // func valueIndex(v ref, i int) ref "syscall/js.valueIndex": (sp) => { sp >>>= 0; diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go index f7e32eb366db81..23df87016e3a7c 100644 --- a/src/syscall/js/js.go +++ b/src/syscall/js/js.go @@ -325,6 +325,60 @@ func (v Value) Delete(p string) { //go:wasmimport gojs syscall/js.valueDelete func valueDelete(v ref, p string) +// GetSymbol returns the JavaScript symbol s of value v. +// It panics if v is not a JavaScript object or s is not a JavaScript symbol. +func (v Value) GetSymbol(s Value) Value { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.GetSymbol", vType}) + } + if sType := s.Type(); sType != TypeSymbol { + panic("syscall/js: Value.GetSymbol: arguement 1 is not a symbol, got " + sType.String()) + } + r := makeValue(valueGetRef(v.ref, s.ref)) + runtime.KeepAlive(v) + runtime.KeepAlive(s) + return r +} + +//go:wasmimport gojs syscall/js.valueGetRef +func valueGetRef(v ref, s ref) ref + +// SetSymbol sets the JavaScript symbol s of value v to ValueOf(x). +// It panics if v is not a JavaScript object or s is not a JavaScript symbol. +func (v Value) SetSymbol(s Value, x any) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.SetSymbol", vType}) + } + if sType := s.Type(); sType != TypeSymbol { + panic("syscall/js: Value.SetSymbol: arguement 1 is not a symbol, got " + sType.String()) + } + xv := ValueOf(x) + valueSetRef(v.ref, s.ref, xv.ref) + runtime.KeepAlive(v) + runtime.KeepAlive(s) + runtime.KeepAlive(xv) +} + +//go:wasmimport gojs syscall/js.valueSetRef +func valueSetRef(v ref, s ref, x ref) + +// DeleteSymbol deletes the JavaScript symbol s of value v. +// It panics if v is not a JavaScript object or s is not a JavaScript symbol. +func (v Value) DeleteSymbol(s Value) { + if vType := v.Type(); !vType.isObject() { + panic(&ValueError{"Value.DeleteSymbol", vType}) + } + if sType := s.Type(); sType != TypeSymbol { + panic("syscall/js: Value.DeleteSymbol: arguement 1 is not a symbol, got " + sType.String()) + } + valueDeleteRef(v.ref, s.ref) + runtime.KeepAlive(v) + runtime.KeepAlive(s) +} + +//go:wasmimport gojs syscall/js.valueDeleteRef +func valueDeleteRef(v ref, s ref) + // Index returns JavaScript index i of value v. // It panics if v is not a JavaScript object. func (v Value) Index(i int) Value { @@ -521,7 +575,8 @@ func (v Value) String() string { case TypeNumber: return "" case TypeSymbol: - return "" + // It's better to print out the symbol name here so we can debug easier + return "" case TypeObject: return "" case TypeFunction: diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index 8823421b894bd0..2308846422251a 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -30,6 +30,8 @@ var dummys = js.Global().Call("eval", `({ someFloat: 42.123, someArray: [41, 42, 43], someDate: new Date(), + someSymbol: Symbol.iterator, + someSymbolFor: Symbol.for("someSymbolFor"), add: function(a, b) { return a + b; }, @@ -97,7 +99,7 @@ func TestString(t *testing.T) { if got, want := js.ValueOf(42.5).String(), ""; got != want { t.Errorf("got %#v, want %#v", got, want) } - if got, want := js.Global().Call("Symbol").String(), ""; got != want { + if got, want := js.Global().Call("Symbol").String(), ""; got != want { t.Errorf("got %#v, want %#v", got, want) } if got, want := js.Global().String(), ""; got != want { @@ -159,6 +161,54 @@ func TestFloat(t *testing.T) { } } +func TestSymbol(t *testing.T) { + if got, want := dummys.Get("someSymbol"), js.Global().Get("Symbol").Get("iterator"); !got.Equal(want) { + t.Errorf("got %#v, want %#v", got, want) + } + if !dummys.Get("someSymbol").Equal(dummys.Get("someSymbol")) { + t.Errorf("same value not equal") + } + if got, want := dummys.Get("someSymbolFor"), js.Global().Get("Symbol").Call("for", "someSymbolFor"); !got.Equal(want) { + t.Errorf("got %#v, want %#v", got, want) + } + if !dummys.Get("someSymbolFor").Equal(dummys.Get("someSymbolFor")) { + t.Errorf("same value not equal") + } + + if iterSym := js.Global().Get("Symbol").Get("iterator"); iterSym.IsUndefined() { + t.Errorf("Symbol.iterator is undefined") + }else{ + o := js.Global().Call("Object") + i := 0 + o.Set("next", js.FuncOf(func(this js.Value, args []js.Value) any { + if i > 3 { + return object{ "done": true, "value": nil } + } + i = i + 1 + return object{ "done": false, "value": 10 - i } + })) + o.SetSymbol(iterSym, js.FuncOf(func(this js.Value, args []js.Value) any { + return o + })) + r := js.Global().Call("eval", `(function(o){ + if (typeof o[Symbol.iterator] !== "function") { + return "object is not iterable" + } + let i = 0 + for (let got of o) { + i++ + let want = 10 - i + if (got !== want) { + return "got " + got + " at iteration #" + i + ", want " + want + } + } + })`).Invoke(o) + if !r.IsUndefined() { + t.Error(r.String()) + } + } +} + func TestObject(t *testing.T) { if !dummys.Get("someArray").Equal(dummys.Get("someArray")) { t.Errorf("same value not equal") From ea927576d7fdc3dfc576adffb2081d209b7d5165 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 11 Jan 2024 14:01:55 -0700 Subject: [PATCH 2/7] run go fmt --- src/syscall/js/js_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index 2308846422251a..3f04bf01759aef 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -177,15 +177,15 @@ func TestSymbol(t *testing.T) { if iterSym := js.Global().Get("Symbol").Get("iterator"); iterSym.IsUndefined() { t.Errorf("Symbol.iterator is undefined") - }else{ + } else { o := js.Global().Call("Object") i := 0 o.Set("next", js.FuncOf(func(this js.Value, args []js.Value) any { if i > 3 { - return object{ "done": true, "value": nil } + return object{"done": true, "value": nil} } i = i + 1 - return object{ "done": false, "value": 10 - i } + return object{"done": false, "value": 10 - i} })) o.SetSymbol(iterSym, js.FuncOf(func(this js.Value, args []js.Value) any { return o From 73366e57b8faa02fe33b92f925246ba08f068c7c Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 11 Jan 2024 14:03:10 -0700 Subject: [PATCH 3/7] use ++ instead of +1 --- src/syscall/js/js_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index 3f04bf01759aef..7cb3af8ee21d6a 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -184,7 +184,7 @@ func TestSymbol(t *testing.T) { if i > 3 { return object{"done": true, "value": nil} } - i = i + 1 + i++ return object{"done": false, "value": 10 - i} })) o.SetSymbol(iterSym, js.FuncOf(func(this js.Value, args []js.Value) any { From e396e101b6c642d0602790dab98ba1ca229f1360 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 11 Jan 2024 14:51:01 -0700 Subject: [PATCH 4/7] should use .Get("Object").New() as standard way even .Call("Object") does not make difference. --- src/syscall/js/js_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index 7cb3af8ee21d6a..fcae5923aee599 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -178,7 +178,7 @@ func TestSymbol(t *testing.T) { if iterSym := js.Global().Get("Symbol").Get("iterator"); iterSym.IsUndefined() { t.Errorf("Symbol.iterator is undefined") } else { - o := js.Global().Call("Object") + o := js.Global().Get("Object").New() i := 0 o.Set("next", js.FuncOf(func(this js.Value, args []js.Value) any { if i > 3 { From 1b3b72bce804ce95322931826b349c3c65a6f9ee Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 11 Jan 2024 14:55:14 -0700 Subject: [PATCH 5/7] test for different Symbol instances equality --- src/syscall/js/js_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index fcae5923aee599..c26fe250f35a33 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -175,6 +175,10 @@ func TestSymbol(t *testing.T) { t.Errorf("same value not equal") } + if js.Global().Call("Symbol").Equal(js.Global().Call("Symbol")) { + t.Errorf("different Symbol instance is equal") + } + if iterSym := js.Global().Get("Symbol").Get("iterator"); iterSym.IsUndefined() { t.Errorf("Symbol.iterator is undefined") } else { From b65eda6e030e51859bf68daed3519d5c0b2b2b03 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 11 Jan 2024 14:55:50 -0700 Subject: [PATCH 6/7] fix English grammer --- src/syscall/js/js_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go index c26fe250f35a33..089c5a3770be76 100644 --- a/src/syscall/js/js_test.go +++ b/src/syscall/js/js_test.go @@ -176,7 +176,7 @@ func TestSymbol(t *testing.T) { } if js.Global().Call("Symbol").Equal(js.Global().Call("Symbol")) { - t.Errorf("different Symbol instance is equal") + t.Errorf("different Symbol instances are equal") } if iterSym := js.Global().Get("Symbol").Get("iterator"); iterSym.IsUndefined() { From d1e5c65f6b3913eb301d2991ed0969f50cf74da1 Mon Sep 17 00:00:00 2001 From: zyxkad Date: Thu, 11 Jan 2024 15:09:34 -0700 Subject: [PATCH 7/7] fix typo arguement -> argument --- src/syscall/js/js.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go index 23df87016e3a7c..3432ade0335954 100644 --- a/src/syscall/js/js.go +++ b/src/syscall/js/js.go @@ -332,7 +332,7 @@ func (v Value) GetSymbol(s Value) Value { panic(&ValueError{"Value.GetSymbol", vType}) } if sType := s.Type(); sType != TypeSymbol { - panic("syscall/js: Value.GetSymbol: arguement 1 is not a symbol, got " + sType.String()) + panic("syscall/js: Value.GetSymbol: argument 1 is not a symbol, got " + sType.String()) } r := makeValue(valueGetRef(v.ref, s.ref)) runtime.KeepAlive(v) @@ -350,7 +350,7 @@ func (v Value) SetSymbol(s Value, x any) { panic(&ValueError{"Value.SetSymbol", vType}) } if sType := s.Type(); sType != TypeSymbol { - panic("syscall/js: Value.SetSymbol: arguement 1 is not a symbol, got " + sType.String()) + panic("syscall/js: Value.SetSymbol: argument 1 is not a symbol, got " + sType.String()) } xv := ValueOf(x) valueSetRef(v.ref, s.ref, xv.ref) @@ -369,7 +369,7 @@ func (v Value) DeleteSymbol(s Value) { panic(&ValueError{"Value.DeleteSymbol", vType}) } if sType := s.Type(); sType != TypeSymbol { - panic("syscall/js: Value.DeleteSymbol: arguement 1 is not a symbol, got " + sType.String()) + panic("syscall/js: Value.DeleteSymbol: argument 1 is not a symbol, got " + sType.String()) } valueDeleteRef(v.ref, s.ref) runtime.KeepAlive(v)