diff --git a/CHANGELOG.md b/CHANGELOG.md index 77f5b240..84830239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Undefined, Null functions to get these constant values for the isolate - Support for calling a method on an object. - Support for calling `IsExecutionTerminating` on isolate to check if execution is still terminating. +- Support for setting and getting internal fields for template object instances ### Changed - Removed error return value from NewIsolate which never fails diff --git a/helpers_test.go b/helpers_test.go index 21c7f772..090d0350 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -8,3 +8,11 @@ func fatalIf(t *testing.T, err error) { t.Fatal(err) } } + +func recoverPanic(f func()) (recovered interface{}) { + defer func() { + recovered = recover() + }() + f() + return nil +} diff --git a/object.go b/object.go index 6c56887b..f9f691ea 100644 --- a/object.go +++ b/object.go @@ -80,9 +80,34 @@ func (o *Object) SetIdx(idx uint32, val interface{}) error { } C.ObjectSetIdx(o.ptr, C.uint32_t(idx), value.ptr) + return nil } +// SetInternalField sets the value of an internal field for an ObjectTemplate instance. +// Panics if the index isn't in the range set by (*ObjectTemplate).SetInternalFieldCount. +func (o *Object) SetInternalField(idx uint32, val interface{}) error { + value, err := coerceValue(o.ctx.iso, val) + + if err != nil { + return err + } + + inserted := C.ObjectSetInternalField(o.ptr, C.uint32_t(idx), value.ptr) + + if inserted == 0 { + panic(fmt.Errorf("index out of range [%v] with length %v", idx, o.InternalFieldCount())) + } + + return nil +} + +// InternalFieldCount returns the number of internal fields this Object has. +func (o *Object) InternalFieldCount() uint32 { + count := C.ObjectInternalFieldCount(o.ptr) + return uint32(count) +} + // Get tries to get a Value for a given Object property key. func (o *Object) Get(key string) (*Value, error) { ckey := C.CString(key) @@ -92,6 +117,18 @@ func (o *Object) Get(key string) (*Value, error) { return valueResult(o.ctx, rtn) } +// GetInternalField gets the Value set by SetInternalField for the given index +// or the JS undefined value if the index hadn't been set. +// Panics if given an out of range index. +func (o *Object) GetInternalField(idx uint32) *Value { + rtn := C.ObjectGetInternalField(o.ptr, C.uint32_t(idx)) + if rtn == nil { + panic(fmt.Errorf("index out of range [%v] with length %v", idx, o.InternalFieldCount())) + } + return &Value{rtn, o.ctx} + +} + // GetIdx tries to get a Value at a give Object index. func (o *Object) GetIdx(idx uint32) (*Value, error) { rtn := C.ObjectGetIdx(o.ptr, C.uint32_t(idx)) diff --git a/object_template.go b/object_template.go index 4b7f677f..c1a7b320 100644 --- a/object_template.go +++ b/object_template.go @@ -60,6 +60,18 @@ func (o *ObjectTemplate) NewInstance(ctx *Context) (*Object, error) { return objectResult(ctx, rtn) } +// SetInternalFieldCount sets the number of internal fields that instances of this +// template will have. +func (o *ObjectTemplate) SetInternalFieldCount(fieldCount uint32) { + C.ObjectTemplateSetInternalFieldCount(o.ptr, C.uint32_t(fieldCount)) +} + +// InternalFieldCount returns the number of internal fields that instances of this +// template will have. +func (o *ObjectTemplate) InternalFieldCount() uint32 { + return uint32(C.ObjectTemplateInternalFieldCount(o.ptr)) +} + func (o *ObjectTemplate) apply(opts *contextOptions) { opts.gTmpl = o } diff --git a/object_test.go b/object_test.go index ca515e73..9daedd9b 100644 --- a/object_test.go +++ b/object_test.go @@ -73,6 +73,53 @@ func TestObjectSet(t *testing.T) { } } +func TestObjectInternalFields(t *testing.T) { + iso := v8.NewIsolate() + defer iso.Dispose() + ctx := v8.NewContext(iso) + defer ctx.Close() + + tmpl := v8.NewObjectTemplate(iso) + obj, err := tmpl.NewInstance(ctx) + fatalIf(t, err) + if count := obj.InternalFieldCount(); count != 0 { + t.Errorf("expected 0 got %v", count) + } + if recoverPanic(func() { obj.GetInternalField(0) }) == nil { + t.Error("expected panic") + } + + tmpl = v8.NewObjectTemplate(iso) + tmpl.SetInternalFieldCount(1) + if count := tmpl.InternalFieldCount(); count != 1 { + t.Errorf("expected 1 got %v", count) + } + + obj, err = tmpl.NewInstance(ctx) + fatalIf(t, err) + if count := obj.InternalFieldCount(); count != 1 { + t.Errorf("expected 1 got %v", count) + } + + if v := obj.GetInternalField(0); !v.SameValue(v8.Undefined(iso)) { + t.Errorf("unexpected value: %q", v) + } + + if err := obj.SetInternalField(0, t); err == nil { + t.Error("expected unsupported value error") + } + + err = obj.SetInternalField(0, "baz") + fatalIf(t, err) + if v := obj.GetInternalField(0); v.String() != "baz" { + t.Errorf("unexpected value: %q", v) + } + + if recoverPanic(func() { obj.SetInternalField(1, "baz") }) == nil { + t.Error("expected panic from index out of bounds") + } +} + func TestObjectGet(t *testing.T) { t.Parallel() diff --git a/v8go.cc b/v8go.cc index 144470fc..8fda5de9 100644 --- a/v8go.cc +++ b/v8go.cc @@ -277,6 +277,20 @@ RtnValue ObjectTemplateNewInstance(TemplatePtr ptr, ContextPtr ctx) { return rtn; } +void ObjectTemplateSetInternalFieldCount(TemplatePtr ptr, uint32_t field_count) { + LOCAL_TEMPLATE(ptr); + + Local obj_tmpl = tmpl.As(); + obj_tmpl->SetInternalFieldCount(field_count); +} + +int ObjectTemplateInternalFieldCount(TemplatePtr ptr) { + LOCAL_TEMPLATE(ptr); + + Local obj_tmpl = tmpl.As(); + return obj_tmpl->InternalFieldCount(); +} + /********** FunctionTemplate **********/ static void FunctionTemplateCallback(const FunctionCallbackInfo& info) { @@ -1048,6 +1062,24 @@ void ObjectSetIdx(ValuePtr ptr, uint32_t idx, ValuePtr prop_val) { obj->Set(local_ctx, idx, prop_val->ptr.Get(iso)).Check(); } +int ObjectSetInternalField(ValuePtr ptr, uint32_t idx, ValuePtr val_ptr) { + LOCAL_OBJECT(ptr); + m_value* prop_val = static_cast(val_ptr); + + if (idx >= obj->InternalFieldCount()) { + return 0; + } + + obj->SetInternalField(idx, prop_val->ptr.Get(iso)); + + return 1; +} + +int ObjectInternalFieldCount(ValuePtr ptr) { + LOCAL_OBJECT(ptr); + return obj->InternalFieldCount(); +} + RtnValue ObjectGet(ValuePtr ptr, const char* key) { LOCAL_OBJECT(ptr); RtnValue rtn = {nullptr, nullptr}; @@ -1073,6 +1105,24 @@ RtnValue ObjectGet(ValuePtr ptr, const char* key) { return rtn; } +ValuePtr ObjectGetInternalField(ValuePtr ptr, uint32_t idx) { + LOCAL_OBJECT(ptr); + + if (idx >= obj->InternalFieldCount()) { + return nullptr; + } + + Local result = obj->GetInternalField(idx); + + m_value* new_val = new m_value; + new_val->iso = iso; + new_val->ctx = ctx; + new_val->ptr = Persistent>( + iso, result); + + return tracked_value(ctx, new_val); +} + RtnValue ObjectGetIdx(ValuePtr ptr, uint32_t idx) { LOCAL_OBJECT(ptr); RtnValue rtn = {nullptr, nullptr}; diff --git a/v8go.h b/v8go.h index 339f424b..371ec10e 100644 --- a/v8go.h +++ b/v8go.h @@ -97,6 +97,8 @@ extern void TemplateSetTemplate(TemplatePtr ptr, extern TemplatePtr NewObjectTemplate(IsolatePtr iso_ptr); extern RtnValue ObjectTemplateNewInstance(TemplatePtr ptr, ContextPtr ctx_ptr); +extern void ObjectTemplateSetInternalFieldCount(TemplatePtr ptr, uint32_t field_count); +extern int ObjectTemplateInternalFieldCount(TemplatePtr ptr); extern TemplatePtr NewFunctionTemplate(IsolatePtr iso_ptr, int callback_ref); extern RtnValue FunctionTemplateGetFunction(TemplatePtr ptr, @@ -183,8 +185,11 @@ int ValueIsModuleNamespaceObject(ValuePtr ptr); extern void ObjectSet(ValuePtr ptr, const char* key, ValuePtr val_ptr); extern void ObjectSetIdx(ValuePtr ptr, uint32_t idx, ValuePtr val_ptr); +extern int ObjectSetInternalField(ValuePtr ptr, uint32_t idx, ValuePtr val_ptr); +extern int ObjectInternalFieldCount(ValuePtr ptr); extern RtnValue ObjectGet(ValuePtr ptr, const char* key); extern RtnValue ObjectGetIdx(ValuePtr ptr, uint32_t idx); +extern ValuePtr ObjectGetInternalField(ValuePtr ptr, uint32_t idx); int ObjectHas(ValuePtr ptr, const char* key); int ObjectHasIdx(ValuePtr ptr, uint32_t idx); int ObjectDelete(ValuePtr ptr, const char* key);