github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_dynamic_test.go (about)

     1  package goja
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  )
     7  
     8  type testDynObject struct {
     9  	r *Runtime
    10  	m map[string]Value
    11  }
    12  
    13  func (t *testDynObject) Get(key string) Value {
    14  	return t.m[key]
    15  }
    16  
    17  func (t *testDynObject) Set(key string, val Value) bool {
    18  	t.m[key] = val
    19  	return true
    20  }
    21  
    22  func (t *testDynObject) Has(key string) bool {
    23  	_, exists := t.m[key]
    24  	return exists
    25  }
    26  
    27  func (t *testDynObject) Delete(key string) bool {
    28  	delete(t.m, key)
    29  	return true
    30  }
    31  
    32  func (t *testDynObject) Keys() []string {
    33  	keys := make([]string, 0, len(t.m))
    34  	for k := range t.m {
    35  		keys = append(keys, k)
    36  	}
    37  	return keys
    38  }
    39  
    40  type testDynArray struct {
    41  	r *Runtime
    42  	a []Value
    43  }
    44  
    45  func (t *testDynArray) Len() int {
    46  	return len(t.a)
    47  }
    48  
    49  func (t *testDynArray) Get(idx int) Value {
    50  	if idx < 0 {
    51  		idx += len(t.a)
    52  	}
    53  	if idx >= 0 && idx < len(t.a) {
    54  		return t.a[idx]
    55  	}
    56  	return nil
    57  }
    58  
    59  func (t *testDynArray) expand(newLen int) {
    60  	if newLen > cap(t.a) {
    61  		a := make([]Value, newLen)
    62  		copy(a, t.a)
    63  		t.a = a
    64  	} else {
    65  		t.a = t.a[:newLen]
    66  	}
    67  }
    68  
    69  func (t *testDynArray) Set(idx int, val Value) bool {
    70  	if idx < 0 {
    71  		idx += len(t.a)
    72  	}
    73  	if idx < 0 {
    74  		return false
    75  	}
    76  	if idx >= len(t.a) {
    77  		t.expand(idx + 1)
    78  	}
    79  	t.a[idx] = val
    80  	return true
    81  }
    82  
    83  func (t *testDynArray) SetLen(i int) bool {
    84  	if i > len(t.a) {
    85  		t.expand(i)
    86  		return true
    87  	}
    88  	if i < 0 {
    89  		return false
    90  	}
    91  	if i < len(t.a) {
    92  		tail := t.a[i:len(t.a)]
    93  		for j := range tail {
    94  			tail[j] = nil
    95  		}
    96  		t.a = t.a[:i]
    97  	}
    98  	return true
    99  }
   100  
   101  func TestDynamicObject(t *testing.T) {
   102  	vm := New()
   103  	dynObj := &testDynObject{
   104  		r: vm,
   105  		m: make(map[string]Value),
   106  	}
   107  	o := vm.NewDynamicObject(dynObj)
   108  	vm.Set("o", o)
   109  	vm.testScriptWithTestLibX(`
   110  	assert(o instanceof Object, "instanceof Object");
   111  	assert(o === o, "self equality");
   112  	assert(o !== {}, "non-equality");
   113  
   114  	o.test = 42;
   115  	assert("test" in o, "'test' in o");
   116  	assert(deepEqual(Object.getOwnPropertyDescriptor(o, "test"), {value: 42, writable: true, enumerable: true, configurable: true}), "prop desc");
   117  
   118  	assert.throws(TypeError, function() {
   119  		"use strict";
   120  		Object.defineProperty(o, "test1", {value: 0, writable: false, enumerable: false, configurable: true});
   121  	}, "define prop");
   122  
   123  	var keys = [];
   124  	for (var key in o) {
   125  		keys.push(key);
   126  	}
   127  	assert(compareArray(keys, ["test"]), "for-in");
   128  
   129  	assert(delete o.test, "delete");
   130  	assert(!("test" in o), "'test' in o after delete");
   131  
   132  	assert("__proto__" in o, "__proto__ in o");
   133  	assert.sameValue(o.__proto__, Object.prototype, "__proto__");
   134  	o.__proto__ = null;
   135  	assert(!("__proto__" in o), "__proto__ in o after setting to null");
   136  	`, _undefined, t)
   137  }
   138  
   139  func TestDynamicObjectCustomProto(t *testing.T) {
   140  	vm := New()
   141  	m := make(map[string]Value)
   142  	dynObj := &testDynObject{
   143  		r: vm,
   144  		m: m,
   145  	}
   146  	o := vm.NewDynamicObject(dynObj)
   147  	vm.Set("o", o)
   148  	vm.testScriptWithTestLib(`
   149  	var proto = {
   150  		valueOf: function() {
   151  			return this.num;
   152  		}
   153  	};
   154  	proto[Symbol.toStringTag] = "GoObject";
   155  	Object.setPrototypeOf(o, proto);
   156  	o.num = 41;
   157  	assert(o instanceof Object, "instanceof");
   158  	assert.sameValue(o+1, 42);
   159  	assert.sameValue(o.toString(), "[object GoObject]");
   160  	`, _undefined, t)
   161  
   162  	if v := m["num"]; v.Export() != int64(41) {
   163  		t.Fatal(v)
   164  	}
   165  }
   166  
   167  func TestDynamicArray(t *testing.T) {
   168  	vm := New()
   169  	dynObj := &testDynArray{
   170  		r: vm,
   171  	}
   172  	a := vm.NewDynamicArray(dynObj)
   173  	vm.Set("a", a)
   174  	vm.testScriptWithTestLibX(`
   175  	assert(a instanceof Array, "instanceof Array");
   176  	assert(a instanceof Object, "instanceof Object");
   177  	assert(a === a, "self equality");
   178  	assert(a !== [], "non-equality");
   179  	assert(Array.isArray(a), "isArray()");
   180  	assert("length" in a, "length in a");
   181  	assert.sameValue(a.length, 0, "len == 0");
   182  	assert.sameValue(a[0], undefined, "a[0] (1)");
   183  
   184  	a[0] = 0;
   185  	assert.sameValue(a[0], 0, "a[0] (2)");
   186  	assert.sameValue(a.length, 1, "length");
   187  	assert(deepEqual(Object.getOwnPropertyDescriptor(a, 0), {value: 0, writable: true, enumerable: true, configurable: true}), "prop desc");
   188  	assert(deepEqual(Object.getOwnPropertyDescriptor(a, "length"), {value: 1, writable: true, enumerable: false, configurable: false}), "length prop desc");
   189  
   190  	assert("__proto__" in a, "__proto__ in a");
   191  	assert.sameValue(a.__proto__, Array.prototype, "__proto__");
   192  
   193  	assert(compareArray(Object.keys(a), ["0"]), "Object.keys()");
   194  	assert(compareArray(Reflect.ownKeys(a), ["0", "length"]), "Reflect.ownKeys()");
   195  
   196  	a.length = 2;
   197  	assert.sameValue(a.length, 2, "length after grow");
   198  	assert.sameValue(a[1], undefined, "a[1]");
   199  
   200  	a[1] = 1;
   201  	assert.sameValue(a[1], 1, "a[1] after set");
   202  	a.length = 1;
   203  	assert.sameValue(a.length, 1, "length after shrink");
   204  	assert.sameValue(a[1], undefined, "a[1] after shrink");
   205  	a.length = 2;
   206  	assert.sameValue(a.length, 2, "length after shrink and grow");
   207  	assert.sameValue(a[1], undefined, "a[1] after grow");
   208  
   209  	a[0] = 3; a[1] = 1; a[2] = 2;
   210  	assert.sameValue(a.length, 3);
   211  	var keys = [];
   212  	for (var key in a) {
   213  		keys.push(key);
   214  	}
   215  	assert(compareArray(keys, ["0","1","2"]), "for-in");
   216  
   217  	var vals = [];
   218  	for (var val of a) {
   219  		vals.push(val);
   220  	}
   221  	assert(compareArray(vals, [3,1,2]), "for-of");
   222  
   223  	a.sort();
   224  	assert(compareArray(a, [1,2,3]), "sort: "+a);
   225  
   226  	assert.sameValue(a[-1], 3);
   227  	assert.sameValue(a[-4], undefined);
   228  
   229  	assert.throws(TypeError, function() {
   230  		"use strict";
   231  		delete a.length;
   232  	}, "delete length");
   233  
   234  	assert.throws(TypeError, function() {
   235  		"use strict";
   236  		a.test = true;
   237  	}, "set string prop");
   238  
   239  	assert.throws(TypeError, function() {
   240  		"use strict";
   241  		Object.defineProperty(a, 0, {value: 0, writable: false, enumerable: false, configurable: true});
   242  	}, "define prop");
   243  
   244  	`, _undefined, t)
   245  }
   246  
   247  type testSharedDynObject struct {
   248  	sync.RWMutex
   249  	m map[string]Value
   250  }
   251  
   252  func (t *testSharedDynObject) Get(key string) Value {
   253  	t.RLock()
   254  	val := t.m[key]
   255  	t.RUnlock()
   256  	return val
   257  }
   258  
   259  func (t *testSharedDynObject) Set(key string, val Value) bool {
   260  	t.Lock()
   261  	t.m[key] = val
   262  	t.Unlock()
   263  	return true
   264  }
   265  
   266  func (t *testSharedDynObject) Has(key string) bool {
   267  	t.RLock()
   268  	_, exists := t.m[key]
   269  	t.RUnlock()
   270  	return exists
   271  }
   272  
   273  func (t *testSharedDynObject) Delete(key string) bool {
   274  	t.Lock()
   275  	delete(t.m, key)
   276  	t.Unlock()
   277  	return true
   278  }
   279  
   280  func (t *testSharedDynObject) Keys() []string {
   281  	t.RLock()
   282  	keys := make([]string, 0, len(t.m))
   283  	for k := range t.m {
   284  		keys = append(keys, k)
   285  	}
   286  	t.RUnlock()
   287  	return keys
   288  }
   289  
   290  func TestSharedDynamicObject(t *testing.T) {
   291  	dynObj := &testSharedDynObject{m: make(map[string]Value, 10000)}
   292  	o := NewSharedDynamicObject(dynObj)
   293  	ch := make(chan error, 1)
   294  	go func() {
   295  		vm := New()
   296  		vm.Set("o", o)
   297  		_, err := vm.RunString(`
   298  			for (let i = 0; i < 10000; i++) {
   299  				o[i] = i;
   300  			}
   301  		`)
   302  		ch <- err
   303  	}()
   304  	vm := New()
   305  	vm.Set("o", o)
   306  	_, err := vm.RunString(`
   307  			for (let i = 0; i < 10000; i++) {
   308  				o[i] = i+1;
   309  			}
   310  	`)
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  
   315  	err = <-ch
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  }
   320  
   321  type testSharedDynArray struct {
   322  	sync.RWMutex
   323  	a []Value
   324  }
   325  
   326  func (t *testSharedDynArray) Len() int {
   327  	t.RLock()
   328  	l := len(t.a)
   329  	t.RUnlock()
   330  	return l
   331  }
   332  
   333  func (t *testSharedDynArray) Get(idx int) Value {
   334  	t.RLock()
   335  	defer t.RUnlock()
   336  	if idx < 0 {
   337  		idx += len(t.a)
   338  	}
   339  	if idx >= 0 && idx < len(t.a) {
   340  		return t.a[idx]
   341  	}
   342  	return nil
   343  }
   344  
   345  func (t *testSharedDynArray) expand(newLen int) {
   346  	if newLen > cap(t.a) {
   347  		a := make([]Value, newLen)
   348  		copy(a, t.a)
   349  		t.a = a
   350  	} else {
   351  		t.a = t.a[:newLen]
   352  	}
   353  }
   354  
   355  func (t *testSharedDynArray) Set(idx int, val Value) bool {
   356  	t.Lock()
   357  	defer t.Unlock()
   358  	if idx < 0 {
   359  		idx += len(t.a)
   360  	}
   361  	if idx < 0 {
   362  		return false
   363  	}
   364  	if idx >= len(t.a) {
   365  		t.expand(idx + 1)
   366  	}
   367  	t.a[idx] = val
   368  	return true
   369  }
   370  
   371  func (t *testSharedDynArray) SetLen(i int) bool {
   372  	t.Lock()
   373  	defer t.Unlock()
   374  	if i > len(t.a) {
   375  		t.expand(i)
   376  		return true
   377  	}
   378  	if i < 0 {
   379  		return false
   380  	}
   381  	if i < len(t.a) {
   382  		tail := t.a[i:len(t.a)]
   383  		for j := range tail {
   384  			tail[j] = nil
   385  		}
   386  		t.a = t.a[:i]
   387  	}
   388  	return true
   389  }
   390  
   391  func TestSharedDynamicArray(t *testing.T) {
   392  	dynObj := &testSharedDynArray{a: make([]Value, 10000)}
   393  	o := NewSharedDynamicArray(dynObj)
   394  	ch := make(chan error, 1)
   395  	go func() {
   396  		vm := New()
   397  		vm.Set("o", o)
   398  		_, err := vm.RunString(`
   399  			for (let i = 0; i < 10000; i++) {
   400  				o[i] = i;
   401  			}
   402  		`)
   403  		ch <- err
   404  	}()
   405  	vm := New()
   406  	vm.Set("o", o)
   407  	_, err := vm.RunString(`
   408  			for (let i = 0; i < 10000; i++) {
   409  				o[i] = i+1;
   410  			}
   411  	`)
   412  	if err != nil {
   413  		t.Fatal(err)
   414  	}
   415  
   416  	err = <-ch
   417  	if err != nil {
   418  		t.Fatal(err)
   419  	}
   420  }