diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1b80bd4..3dd73c5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Support for [binary strings](https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary). `NewStringFromByteArray` takes bytes array as input and uses `String::NewFromOneByte` to convert each byte to an equivalent character. + ### Fixed - Use string length to ensure null character-containing strings in Go/JS are not terminated early. - Object.Set with an empty key string is now supported diff --git a/value.go b/value.go index dc65daa0f..fd61d9e2f 100644 --- a/value.go +++ b/value.go @@ -53,9 +53,10 @@ func Null(iso *Isolate) *Value { return iso.null } +// NewStringFromByteArray returns V8::string from byte array. Converts each byte into equivalent character. func NewStringFromByteArray(iso *Isolate, val []byte) (*Value, error) { if iso == nil { - return nil, errors.New("v8go: failed to create new Value: Isolate cannot be ") + panic(errors.New("v8go: failed to create new Value: Isolate cannot be ")) } cUint := (*C.uchar)(unsafe.Pointer(&val[0])) rtnVal := C.NewValueStringFromByteArray(iso.ptr, cUint, C.int(len(val))) diff --git a/value_test.go b/value_test.go index 14b75fe5b..bb599660b 100644 --- a/value_test.go +++ b/value_test.go @@ -6,7 +6,6 @@ package v8go_test import ( "bytes" - "encoding/base64" "fmt" "math" "math/big" @@ -522,23 +521,55 @@ func TestValueFunction(t *testing.T) { func TestNewStringFromByteArray(t *testing.T) { t.Parallel() - ctx := v8.NewContext() - iso := ctx.Isolate() - - inputString := "CN7ySxWjjWNpbYPB1n/TBR8avujZS2cHdXRR5ZM7Fi6QBjlVzBqPu0CI9pQXcW9fvbMXdqU+57XY/QdGozJT19+gbQDM0ZQVzjwqtpyLorcPHjMqum+7dHD6XF5cXo3NZlKGsxcnvSVyClBDU5M1dUCe8bB9yV0wVM6ge+0WAmTX2GYbncilTjDw0bSJI1Z+71NT8UQCfmimKhVxJiKrnkaTrTw2Ma/1I2w4Dny3cRlFtCtob9cvNOeeIm8HtQoi/7HXoE0uFr1C39OL2hCC1TJsxX94djtNFqd9aUOPYrwT+zErSokSvbNYS5WpEjEpRJze9+TCV9NLmqCnARK4Bw" - byts, _ := base64.RawStdEncoding.DecodeString(inputString) + iso := v8.NewIsolate() + defer iso.Dispose() + global := v8.NewObjectTemplate(iso) + + testFn := v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value { + args := info.Args() + input := args[0].String() + if len(input) == 0 { + //String() terminates input at null character. hence hardcoding for 3rd input + input = "a\x00\xffe" + } + val, _ := v8.NewStringFromByteArray(iso, []byte(input)) + return val + }) + global.Set("foo", testFn, v8.ReadOnly) - expected := "ÞòK£cimƒÁÖÓ¾èÙKgutQå“;.9Ȕ»@ˆö”qo_½³v¥>çµØýF£2S×ß m" - val, err := v8.NewStringFromByteArray(iso, byts) - if err != nil { - t.Errorf("expected nil but got error %#v", err) - } - if !val.IsString() { - t.Errorf("expected string but got %s", reflect.TypeOf(val)) + ctx := v8.NewContext(iso, global) + defer ctx.Close() + tests := [...]struct { + name string + script string + }{ + {"normal string", + ` let res = foo("str"); + if(res.length != 3){ + throw Error("expected length 3") + } + `}, + {"multi-byte sequence", + ` res = foo("Ò"); + if(res.length != 2 ){ + throw Error("expected length 2") + } + `}, + {"null terminated sequence", + ` res = foo(""); + if(res.length != 4){ + throw Error("expected length 4") + } + `}, } - if val.String() != expected { - t.Errorf("expected not same as actual") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if _, err := ctx.RunScript(tt.script, ""); err != nil { + t.Errorf("expected error, but got error: %v", err) + } + }) } + } func TestValueSameValue(t *testing.T) {