Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert byte array to string #266

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- NewStringFromBytes to support the creation of [binary strings](https://developer.mozilla.org/en-US/docs/Web/API/DOMString/Binary) from Go byte array.

### 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
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module rogchap.com/v8go

go 1.16
go 1.16
18 changes: 18 additions & 0 deletions v8go.cc
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,23 @@ ValuePtr NewValueIntegerFromUnsigned(IsolatePtr iso, uint32_t v) {
return tracked_value(ctx, val);
}

RtnValue NewStringFromBytes(IsolatePtr iso, const uint8_t* v, int len){
ISOLATE_SCOPE_INTERNAL_CONTEXT(iso);
TryCatch try_catch(iso);
RtnValue rtn = {};
Local<String> str;
if (!String::NewFromOneByte(iso, v, NewStringType::kNormal, len).ToLocal(&str)) {
rtn.error = ExceptionError(try_catch, iso, ctx->ptr.Get(iso));
return rtn;
}
m_value* val = new m_value;
val->iso = iso;
val->ctx = ctx;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, str);
rtn.value = tracked_value(ctx, val);
return rtn;
}

RtnValue NewValueString(IsolatePtr iso, const char* v, int v_length) {
ISOLATE_SCOPE_INTERNAL_CONTEXT(iso);
TryCatch try_catch(iso);
Expand Down Expand Up @@ -962,6 +979,7 @@ RtnString ValueToString(ValuePtr ptr) {
// TODO: Consider propagating the JS error. A fallback value could be returned
// in Value.String()
String::Utf8Value src(iso, value);
printf("___ valuetostring %d \n", src.length());
char* data = static_cast<char*>(malloc(src.length()));
memcpy(data, *src, src.length());
rtn.data = data;
Expand Down
1 change: 1 addition & 0 deletions v8go.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ extern ValuePtr NewValueUndefined(IsolatePtr iso_ptr);
extern ValuePtr NewValueInteger(IsolatePtr iso_ptr, int32_t v);
extern ValuePtr NewValueIntegerFromUnsigned(IsolatePtr iso_ptr, uint32_t v);
extern RtnValue NewValueString(IsolatePtr iso_ptr, const char* v, int v_length);
extern RtnValue NewStringFromBytes(IsolatePtr iso, const uint8_t* v, int len);
extern ValuePtr NewValueBoolean(IsolatePtr iso_ptr, int v);
extern ValuePtr NewValueNumber(IsolatePtr iso_ptr, double v);
extern ValuePtr NewValueBigInt(IsolatePtr iso_ptr, int64_t v);
Expand Down
10 changes: 10 additions & 0 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ func Null(iso *Isolate) *Value {
return iso.null
}

// NewStringFromBytes returns V8::string from byte array. Creates binary string from bytes.
func NewStringFromBytes(iso *Isolate, bytes []byte) (*Value, error) {
if iso == nil {
panic(errors.New("v8go: failed to create new Value: Isolate cannot be <nil>"))
}
cUint := (*C.uchar)(unsafe.Pointer(&bytes[0]))
rtnVal := C.NewStringFromBytes(iso.ptr, cUint, C.int(len(bytes)))
return valueResult(nil, rtnVal)
}

// NewValue will create a primitive value. Supported values types to create are:
// string -> V8::String
// int32 -> V8::Integer
Expand Down
57 changes: 57 additions & 0 deletions value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,63 @@ func TestValueFunction(t *testing.T) {

}

func TestNewStringFromBytes(t *testing.T) {
t.Parallel()
iso := v8.NewIsolate()
defer iso.Dispose()
global := v8.NewObjectTemplate(iso)

if recoverPanic(func() { v8.NewStringFromBytes(nil, []byte{}) }) == nil {
t.Error("expected panic")
}

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A workaround is no longer needed for this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.String() not working as expected. for input string a\x00\xffe, it should return 4 bytes instead getting 5.

input = "a\x00\xffe"
}
val, _ := v8.NewStringFromBytes(iso, []byte(input))
return val
})
global.Set("foo", testFn, v8.ReadOnly)

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")
}
`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if _, err := ctx.RunScript(tt.script, ""); err != nil {
t.Errorf("expected <nil> error, but got error: %v", err)
}
})
}

}

func TestValueSameValue(t *testing.T) {
t.Parallel()
iso := v8.NewIsolate()
Expand Down