ai_old/goja/object_dynamic_test.go

421 lines
8.1 KiB
Go
Raw Normal View History

2024-09-20 16:50:35 +08:00
package goja
import (
"sync"
"testing"
)
type testDynObject struct {
r *Runtime
m map[string]Value
}
func (t *testDynObject) Get(key string) Value {
return t.m[key]
}
func (t *testDynObject) Set(key string, val Value) bool {
t.m[key] = val
return true
}
func (t *testDynObject) Has(key string) bool {
_, exists := t.m[key]
return exists
}
func (t *testDynObject) Delete(key string) bool {
delete(t.m, key)
return true
}
func (t *testDynObject) Keys() []string {
keys := make([]string, 0, len(t.m))
for k := range t.m {
keys = append(keys, k)
}
return keys
}
type testDynArray struct {
r *Runtime
a []Value
}
func (t *testDynArray) Len() int {
return len(t.a)
}
func (t *testDynArray) Get(idx int) Value {
if idx < 0 {
idx += len(t.a)
}
if idx >= 0 && idx < len(t.a) {
return t.a[idx]
}
return nil
}
func (t *testDynArray) expand(newLen int) {
if newLen > cap(t.a) {
a := make([]Value, newLen)
copy(a, t.a)
t.a = a
} else {
t.a = t.a[:newLen]
}
}
func (t *testDynArray) Set(idx int, val Value) bool {
if idx < 0 {
idx += len(t.a)
}
if idx < 0 {
return false
}
if idx >= len(t.a) {
t.expand(idx + 1)
}
t.a[idx] = val
return true
}
func (t *testDynArray) SetLen(i int) bool {
if i > len(t.a) {
t.expand(i)
return true
}
if i < 0 {
return false
}
if i < len(t.a) {
tail := t.a[i:len(t.a)]
for j := range tail {
tail[j] = nil
}
t.a = t.a[:i]
}
return true
}
func TestDynamicObject(t *testing.T) {
vm := New()
dynObj := &testDynObject{
r: vm,
m: make(map[string]Value),
}
o := vm.NewDynamicObject(dynObj)
vm.Set("o", o)
vm.testScriptWithTestLibX(`
assert(o instanceof Object, "instanceof Object");
assert(o === o, "self equality");
assert(o !== {}, "non-equality");
o.test = 42;
assert("test" in o, "'test' in o");
assert(deepEqual(Object.getOwnPropertyDescriptor(o, "test"), {value: 42, writable: true, enumerable: true, configurable: true}), "prop desc");
assert.throws(TypeError, function() {
"use strict";
Object.defineProperty(o, "test1", {value: 0, writable: false, enumerable: false, configurable: true});
}, "define prop");
var keys = [];
for (var key in o) {
keys.push(key);
}
assert(compareArray(keys, ["test"]), "for-in");
assert(delete o.test, "delete");
assert(!("test" in o), "'test' in o after delete");
assert("__proto__" in o, "__proto__ in o");
assert.sameValue(o.__proto__, Object.prototype, "__proto__");
o.__proto__ = null;
assert(!("__proto__" in o), "__proto__ in o after setting to null");
`, _undefined, t)
}
func TestDynamicObjectCustomProto(t *testing.T) {
vm := New()
m := make(map[string]Value)
dynObj := &testDynObject{
r: vm,
m: m,
}
o := vm.NewDynamicObject(dynObj)
vm.Set("o", o)
vm.testScriptWithTestLib(`
var proto = {
valueOf: function() {
return this.num;
}
};
proto[Symbol.toStringTag] = "GoObject";
Object.setPrototypeOf(o, proto);
o.num = 41;
assert(o instanceof Object, "instanceof");
assert.sameValue(o+1, 42);
assert.sameValue(o.toString(), "[object GoObject]");
`, _undefined, t)
if v := m["num"]; v.Export() != int64(41) {
t.Fatal(v)
}
}
func TestDynamicArray(t *testing.T) {
vm := New()
dynObj := &testDynArray{
r: vm,
}
a := vm.NewDynamicArray(dynObj)
vm.Set("a", a)
vm.testScriptWithTestLibX(`
assert(a instanceof Array, "instanceof Array");
assert(a instanceof Object, "instanceof Object");
assert(a === a, "self equality");
assert(a !== [], "non-equality");
assert(Array.isArray(a), "isArray()");
assert("length" in a, "length in a");
assert.sameValue(a.length, 0, "len == 0");
assert.sameValue(a[0], undefined, "a[0] (1)");
a[0] = 0;
assert.sameValue(a[0], 0, "a[0] (2)");
assert.sameValue(a.length, 1, "length");
assert(deepEqual(Object.getOwnPropertyDescriptor(a, 0), {value: 0, writable: true, enumerable: true, configurable: true}), "prop desc");
assert(deepEqual(Object.getOwnPropertyDescriptor(a, "length"), {value: 1, writable: true, enumerable: false, configurable: false}), "length prop desc");
assert("__proto__" in a, "__proto__ in a");
assert.sameValue(a.__proto__, Array.prototype, "__proto__");
assert(compareArray(Object.keys(a), ["0"]), "Object.keys()");
assert(compareArray(Reflect.ownKeys(a), ["0", "length"]), "Reflect.ownKeys()");
a.length = 2;
assert.sameValue(a.length, 2, "length after grow");
assert.sameValue(a[1], undefined, "a[1]");
a[1] = 1;
assert.sameValue(a[1], 1, "a[1] after set");
a.length = 1;
assert.sameValue(a.length, 1, "length after shrink");
assert.sameValue(a[1], undefined, "a[1] after shrink");
a.length = 2;
assert.sameValue(a.length, 2, "length after shrink and grow");
assert.sameValue(a[1], undefined, "a[1] after grow");
a[0] = 3; a[1] = 1; a[2] = 2;
assert.sameValue(a.length, 3);
var keys = [];
for (var key in a) {
keys.push(key);
}
assert(compareArray(keys, ["0","1","2"]), "for-in");
var vals = [];
for (var val of a) {
vals.push(val);
}
assert(compareArray(vals, [3,1,2]), "for-of");
a.sort();
assert(compareArray(a, [1,2,3]), "sort: "+a);
assert.sameValue(a[-1], 3);
assert.sameValue(a[-4], undefined);
assert.throws(TypeError, function() {
"use strict";
delete a.length;
}, "delete length");
assert.throws(TypeError, function() {
"use strict";
a.test = true;
}, "set string prop");
assert.throws(TypeError, function() {
"use strict";
Object.defineProperty(a, 0, {value: 0, writable: false, enumerable: false, configurable: true});
}, "define prop");
`, _undefined, t)
}
type testSharedDynObject struct {
sync.RWMutex
m map[string]Value
}
func (t *testSharedDynObject) Get(key string) Value {
t.RLock()
val := t.m[key]
t.RUnlock()
return val
}
func (t *testSharedDynObject) Set(key string, val Value) bool {
t.Lock()
t.m[key] = val
t.Unlock()
return true
}
func (t *testSharedDynObject) Has(key string) bool {
t.RLock()
_, exists := t.m[key]
t.RUnlock()
return exists
}
func (t *testSharedDynObject) Delete(key string) bool {
t.Lock()
delete(t.m, key)
t.Unlock()
return true
}
func (t *testSharedDynObject) Keys() []string {
t.RLock()
keys := make([]string, 0, len(t.m))
for k := range t.m {
keys = append(keys, k)
}
t.RUnlock()
return keys
}
func TestSharedDynamicObject(t *testing.T) {
dynObj := &testSharedDynObject{m: make(map[string]Value, 10000)}
o := NewSharedDynamicObject(dynObj)
ch := make(chan error, 1)
go func() {
vm := New()
vm.Set("o", o)
_, err := vm.RunString(`
for (let i = 0; i < 10000; i++) {
o[i] = i;
}
`)
ch <- err
}()
vm := New()
vm.Set("o", o)
_, err := vm.RunString(`
for (let i = 0; i < 10000; i++) {
o[i] = i+1;
}
`)
if err != nil {
t.Fatal(err)
}
err = <-ch
if err != nil {
t.Fatal(err)
}
}
type testSharedDynArray struct {
sync.RWMutex
a []Value
}
func (t *testSharedDynArray) Len() int {
t.RLock()
l := len(t.a)
t.RUnlock()
return l
}
func (t *testSharedDynArray) Get(idx int) Value {
t.RLock()
defer t.RUnlock()
if idx < 0 {
idx += len(t.a)
}
if idx >= 0 && idx < len(t.a) {
return t.a[idx]
}
return nil
}
func (t *testSharedDynArray) expand(newLen int) {
if newLen > cap(t.a) {
a := make([]Value, newLen)
copy(a, t.a)
t.a = a
} else {
t.a = t.a[:newLen]
}
}
func (t *testSharedDynArray) Set(idx int, val Value) bool {
t.Lock()
defer t.Unlock()
if idx < 0 {
idx += len(t.a)
}
if idx < 0 {
return false
}
if idx >= len(t.a) {
t.expand(idx + 1)
}
t.a[idx] = val
return true
}
func (t *testSharedDynArray) SetLen(i int) bool {
t.Lock()
defer t.Unlock()
if i > len(t.a) {
t.expand(i)
return true
}
if i < 0 {
return false
}
if i < len(t.a) {
tail := t.a[i:len(t.a)]
for j := range tail {
tail[j] = nil
}
t.a = t.a[:i]
}
return true
}
func TestSharedDynamicArray(t *testing.T) {
dynObj := &testSharedDynArray{a: make([]Value, 10000)}
o := NewSharedDynamicArray(dynObj)
ch := make(chan error, 1)
go func() {
vm := New()
vm.Set("o", o)
_, err := vm.RunString(`
for (let i = 0; i < 10000; i++) {
o[i] = i;
}
`)
ch <- err
}()
vm := New()
vm.Set("o", o)
_, err := vm.RunString(`
for (let i = 0; i < 10000; i++) {
o[i] = i+1;
}
`)
if err != nil {
t.Fatal(err)
}
err = <-ch
if err != nil {
t.Fatal(err)
}
}