v8go icon indicating copy to clipboard operation
v8go copied to clipboard

How to construct an array and pass it in through global

Open mitar opened this issue 3 years ago • 11 comments

I could not find any documentation how to construct an array and pass it in through global.

mitar avatar May 07 '21 05:05 mitar

Currently not supported by the Go API, but you can create a array on the JS side and attach it to the global object. I'm looking to add support for this from Go soon.

rogchap avatar Jun 05 '21 04:06 rogchap

What I ended up doing was just faking it with the object where I set numeric properties. Worked for my limited use case.

mitar avatar Jun 05 '21 04:06 mitar

I have an interest in array support as well.

jacquayj avatar Jul 08 '21 02:07 jacquayj

it should be actually rather simple:

type Array struct {
	*Object
}

func NewArray(ctx *ExecContext, len int64) *Array {
	val := &Value{C.NewArray(ctx.iso.ptr, C.size_t(len)), ctx}
	return &Array{Object: &Object{Value: val}}
}
// Create a new NewArray value of the requested size
ValuePtr NewArray(IsolatePtr iso_ptr, size_t length) {
  ISOLATE_SCOPE_INTERNAL_CONTEXT(iso_ptr);
  Local<Context> c = ctx->ptr.Get(iso);
  c->Enter();

  Local<Array> arr = Array::New(iso, length);

  m_value* val = new m_value;
  val->iso = iso;
  val->ctx = ctx;
  val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(iso, arr);

  return tracked_value(ctx, val);
}

should do it, since Array is an object, Set() is available.

huttarichard avatar Jul 16 '21 11:07 huttarichard

I have found a way to do this without object template:

// first at all, we create a new Array
arrayValue, err := ctx.RunScript("new Array()", "")
if err != nil {
    return nil, err
}
var obj = arrayValue.Object()

// set array elements count
err = obj.Set("length", 10)
if err != nil {
    return nil, err
}

// we add elements to the array
obj.SetIdx(0, "value1")
obj.SetIdx(1, "value2")
...

// now, the resultArray is you wanted
var resultArray = obj.Value

iwind avatar Dec 26 '21 03:12 iwind

Probably the number one lacking feature.

workarounds are nice, kind of surprised it isn't part of the default API though

keyboardsmoke avatar Jan 27 '22 15:01 keyboardsmoke

v8::Array::New is an overloaded function, so we should consider how we want to expose the two ways it can be called. It looks like they correspond to the JS Array constructors.

One function creates an array with provided values. It seems like this would map well to variadic functions func NewArray(values ...v8go.Valuer) Array. Or is there a concern with efficiency with passing a large Go slice like this as variadic arguments? If there ends up being problems with efficiency with variadic arguments, we could always extract the implementation into func NewArrayFromSlice(values []v8go.Value) Array as an alternative.

The other function creates an array of a given length without initializing the values. Perhaps this could be NewArrayWithLength(length uint32) Array.

@huttarichard you had named this function NewArray in your comment, is that because you believe this would be more commonly used? It seems like the variadic function corresponds more closely corresponds with creating a literal Array, which seemed like it was more deserving of brief usage.

Does anyone else have opinions on the v8go API for constructing an array?

dylanahsmith avatar Jan 28 '22 23:01 dylanahsmith

@dylanahsmith Have you thought about adding slice/array support to the v8go.NewValue method? I think this would provide the best developer experience. This was also the first place I looked at, while trying to find out how to create a JS array via Go.

hello, _ := v8go.NewValue(iso, "hello")
world, _ := v8go.NewValue(iso, "world")
_, _ = v8go.NewValue(iso, []string{"hello", "world"})
_, _ = v8go.NewValue(iso, []*v8.Value{hello, world})

jmattheis avatar Feb 21 '22 18:02 jmattheis

Once there is a NewArrayFromSlice-like function, then NewValue could delegate to it.

However, NewValue suffers from a lack of type safety, which can lead to a lack of clarity on where errors may be returned. This can lead to errors being ignored, which makes debugging more difficult or to verbose unused error handling code. Even when it is known that the error isn't needed, an assignment is needed to ignore the error rather than allowing the value to be used directly. The more general v8go.Value type also gets returned, which can lead to a dynamic cast to be needed, which also returns an error.

dylanahsmith avatar Feb 22 '22 16:02 dylanahsmith

You're right, but the addition of slice support for NewValue wouldn't make it worse, as the type safety problems already exist. Maybe NewValue should be deprecated and specialized methods for every type should be added.

func NewStringValue(s string) *v8go.Value
func NewIntValue(i int) *v8go.Value

jmattheis avatar Feb 22 '22 17:02 jmattheis

@iwind , I dig the idea of using javascript to build on v8go, so I generified your idea a little as a workaround to support http.Header. Though I, too, would very much enjoy native support, @dylanahsmith . I wonder how bad the performance of JSON.parse will impact.

func parseToValue(vm *v8.Isolate, in interface{}) (*v8.Value, error) {
	bytes, err := json.Marshal(in)
	if err != nil {
		return nil, err
	}
	ctx := v8.NewContext(vm)
	res, err := ctx.RunScript(fmt.Sprintf("JSON.parse(`%s`)", string(bytes)), "parse_to_value")
	if err != nil {
		return nil, err
	}
	return res.Object().Value, err
}

ti2ger92 avatar May 29 '22 13:05 ti2ger92