refactor(common): 重构 Conn 实体并优化地图进入逻辑
- 优化 Conn 实体的 SendPack 方法,提高代码复用性 - 添加 goja 模块到 go.work 文件 - 重构地图进入逻辑,增加玩家广播和刷怪功能 - 调整 OutInfo 结构中的 Vip 和 Viped 字段类型 - 简化 MonsterRefresh 结构体定义
This commit is contained in:
660
common/utils/goja/object_test.go
Normal file
660
common/utils/goja/object_test.go
Normal file
@@ -0,0 +1,660 @@
|
||||
package goja
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefineProperty(t *testing.T) {
|
||||
r := New()
|
||||
o := r.NewObject()
|
||||
|
||||
err := o.DefineDataProperty("data", r.ToValue(42), FLAG_TRUE, FLAG_TRUE, FLAG_TRUE)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = o.DefineAccessorProperty("accessor_ro", r.ToValue(func() int {
|
||||
return 1
|
||||
}), nil, FLAG_TRUE, FLAG_TRUE)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = o.DefineAccessorProperty("accessor_rw",
|
||||
r.ToValue(func(call FunctionCall) Value {
|
||||
return o.Get("__hidden")
|
||||
}),
|
||||
r.ToValue(func(call FunctionCall) (ret Value) {
|
||||
o.Set("__hidden", call.Argument(0))
|
||||
return
|
||||
}),
|
||||
FLAG_TRUE, FLAG_TRUE)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v := o.Get("accessor_ro"); v.ToInteger() != 1 {
|
||||
t.Fatalf("Unexpected accessor value: %v", v)
|
||||
}
|
||||
|
||||
err = o.Set("accessor_ro", r.ToValue(2))
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
if ex, ok := err.(*Exception); ok {
|
||||
if msg := ex.Error(); msg != "TypeError: Cannot assign to read only property 'accessor_ro'" {
|
||||
t.Fatalf("Unexpected error: '%s'", msg)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Unexected error type: %T", err)
|
||||
}
|
||||
|
||||
err = o.Set("accessor_rw", 42)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v := o.Get("accessor_rw"); v.ToInteger() != 42 {
|
||||
t.Fatalf("Unexpected value: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPropertyOrder(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var o = {};
|
||||
var sym1 = Symbol(1);
|
||||
var sym2 = Symbol(2);
|
||||
o[sym2] = 1;
|
||||
o[4294967294] = 1;
|
||||
o[2] = 1;
|
||||
o[1] = 1;
|
||||
o[0] = 1;
|
||||
o["02"] = 1;
|
||||
o[4294967295] = 1;
|
||||
o["01"] = 1;
|
||||
o["00"] = 1;
|
||||
o[sym1] = 1;
|
||||
var expected = ["0", "1", "2", "4294967294", "02", "4294967295", "01", "00", sym2, sym1];
|
||||
var actual = Reflect.ownKeys(o);
|
||||
if (actual.length !== expected.length) {
|
||||
throw new Error("Unexpected length: "+actual.length);
|
||||
}
|
||||
for (var i = 0; i < actual.length; i++) {
|
||||
if (actual[i] !== expected[i]) {
|
||||
throw new Error("Unexpected list: " + actual);
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
testScript(SCRIPT, _undefined, t)
|
||||
}
|
||||
|
||||
func TestDefinePropertiesSymbol(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var desc = {};
|
||||
desc[Symbol.toStringTag] = {value: "Test"};
|
||||
var o = {};
|
||||
Object.defineProperties(o, desc);
|
||||
o[Symbol.toStringTag] === "Test";
|
||||
`
|
||||
|
||||
testScript(SCRIPT, valueTrue, t)
|
||||
}
|
||||
|
||||
func TestObjectShorthandProperties(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var b = 1;
|
||||
var a = {b, get() {return "c"}};
|
||||
|
||||
assert.sameValue(a.b, b, "#1");
|
||||
assert.sameValue(a.get(), "c", "#2");
|
||||
|
||||
var obj = {
|
||||
w\u0069th() { return 42; }
|
||||
};
|
||||
|
||||
assert.sameValue(obj['with'](), 42, 'property exists');
|
||||
`
|
||||
testScriptWithTestLib(SCRIPT, _undefined, t)
|
||||
}
|
||||
|
||||
func TestObjectAssign(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
assert.sameValue(Object.assign({ b: 1 }, { get a() {
|
||||
Object.defineProperty(this, "b", {
|
||||
value: 3,
|
||||
enumerable: false
|
||||
});
|
||||
}, b: 2 }).b, 1, "#1");
|
||||
|
||||
assert.sameValue(Object.assign({ b: 1 }, { get a() {
|
||||
delete this.b;
|
||||
}, b: 2 }).b, 1, "#2");
|
||||
`
|
||||
testScriptWithTestLib(SCRIPT, _undefined, t)
|
||||
}
|
||||
|
||||
func TestExportCircular(t *testing.T) {
|
||||
vm := New()
|
||||
o := vm.NewObject()
|
||||
o.Set("o", o)
|
||||
v := o.Export()
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
if reflect.ValueOf(m["o"]).Pointer() != reflect.ValueOf(v).Pointer() {
|
||||
t.Fatal("Unexpected value")
|
||||
}
|
||||
} else {
|
||||
t.Fatal("Unexpected type")
|
||||
}
|
||||
|
||||
res, err := vm.RunString(`var a = []; a[0] = a;`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v = res.Export()
|
||||
if a, ok := v.([]interface{}); ok {
|
||||
if reflect.ValueOf(a[0]).Pointer() != reflect.ValueOf(v).Pointer() {
|
||||
t.Fatal("Unexpected value")
|
||||
}
|
||||
} else {
|
||||
t.Fatal("Unexpected type")
|
||||
}
|
||||
}
|
||||
|
||||
type test_s struct {
|
||||
S *test_s1
|
||||
}
|
||||
type test_s1 struct {
|
||||
S *test_s
|
||||
}
|
||||
|
||||
func TestExportToCircular(t *testing.T) {
|
||||
vm := New()
|
||||
o := vm.NewObject()
|
||||
o.Set("o", o)
|
||||
var m map[string]interface{}
|
||||
err := vm.ExportTo(o, &m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type K string
|
||||
type T map[K]T
|
||||
var m1 T
|
||||
err = vm.ExportTo(o, &m1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type A []A
|
||||
var a A
|
||||
res, err := vm.RunString("var a = []; a[0] = a;")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = vm.ExportTo(res, &a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if &a[0] != &a[0][0] {
|
||||
t.Fatal("values do not match")
|
||||
}
|
||||
|
||||
o = vm.NewObject()
|
||||
o.Set("S", o)
|
||||
var s test_s
|
||||
err = vm.ExportTo(o, &s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s.S.S != &s {
|
||||
t.Fatalf("values do not match: %v, %v", s.S.S, &s)
|
||||
}
|
||||
|
||||
type test_s2 struct {
|
||||
S interface{}
|
||||
S1 *test_s2
|
||||
}
|
||||
|
||||
var s2 test_s2
|
||||
o.Set("S1", o)
|
||||
|
||||
err = vm.ExportTo(o, &s2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if m, ok := s2.S.(map[string]interface{}); ok {
|
||||
if reflect.ValueOf(m["S"]).Pointer() != reflect.ValueOf(m).Pointer() {
|
||||
t.Fatal("Unexpected m.S")
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Unexpected s2.S type: %T", s2.S)
|
||||
}
|
||||
if s2.S1 != &s2 {
|
||||
t.Fatal("Unexpected s2.S1")
|
||||
}
|
||||
|
||||
o1 := vm.NewObject()
|
||||
o1.Set("S", o)
|
||||
o1.Set("S1", o)
|
||||
err = vm.ExportTo(o1, &s2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s2.S1.S1 != s2.S1 {
|
||||
t.Fatal("Unexpected s2.S1.S1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportWrappedMap(t *testing.T) {
|
||||
vm := New()
|
||||
m := map[string]interface{}{
|
||||
"test": "failed",
|
||||
}
|
||||
exported := vm.ToValue(m).Export()
|
||||
if exportedMap, ok := exported.(map[string]interface{}); ok {
|
||||
exportedMap["test"] = "passed"
|
||||
if v := m["test"]; v != "passed" {
|
||||
t.Fatalf("Unexpected m[\"test\"]: %v", v)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Unexpected export type: %T", exported)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportToWrappedMap(t *testing.T) {
|
||||
vm := New()
|
||||
m := map[string]interface{}{
|
||||
"test": "failed",
|
||||
}
|
||||
var exported map[string]interface{}
|
||||
err := vm.ExportTo(vm.ToValue(m), &exported)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exported["test"] = "passed"
|
||||
if v := m["test"]; v != "passed" {
|
||||
t.Fatalf("Unexpected m[\"test\"]: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportToWrappedMapCustom(t *testing.T) {
|
||||
type CustomMap map[string]bool
|
||||
vm := New()
|
||||
m := CustomMap{}
|
||||
var exported CustomMap
|
||||
err := vm.ExportTo(vm.ToValue(m), &exported)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
exported["test"] = true
|
||||
if v := m["test"]; v != true {
|
||||
t.Fatalf("Unexpected m[\"test\"]: %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportToSliceNonIterable(t *testing.T) {
|
||||
vm := New()
|
||||
o := vm.NewObject()
|
||||
var a []interface{}
|
||||
err := vm.ExportTo(o, &a)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error")
|
||||
}
|
||||
if len(a) != 0 {
|
||||
t.Fatalf("a: %v", a)
|
||||
}
|
||||
if msg := err.Error(); msg != "cannot convert [object Object] to []interface {}: not an array or iterable" {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRuntime_ExportTo_iterableToSlice() {
|
||||
vm := New()
|
||||
v, err := vm.RunString(`
|
||||
function reverseIterator() {
|
||||
const arr = this;
|
||||
let idx = arr.length;
|
||||
return {
|
||||
next: () => idx > 0 ? {value: arr[--idx]} : {done: true}
|
||||
}
|
||||
}
|
||||
const arr = [1,2,3];
|
||||
arr[Symbol.iterator] = reverseIterator;
|
||||
arr;
|
||||
`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var arr []int
|
||||
err = vm.ExportTo(v, &arr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(arr)
|
||||
// Output: [3 2 1]
|
||||
}
|
||||
|
||||
func TestRuntime_ExportTo_proxiedIterableToSlice(t *testing.T) {
|
||||
vm := New()
|
||||
v, err := vm.RunString(`
|
||||
function reverseIterator() {
|
||||
const arr = this;
|
||||
let idx = arr.length;
|
||||
return {
|
||||
next: () => idx > 0 ? {value: arr[--idx]} : {done: true}
|
||||
}
|
||||
}
|
||||
const arr = [1,2,3];
|
||||
arr[Symbol.iterator] = reverseIterator;
|
||||
new Proxy(arr, {});
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var arr []int
|
||||
err = vm.ExportTo(v, &arr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out := fmt.Sprint(arr); out != "[3 2 1]" {
|
||||
t.Fatal(out)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRuntime_ExportTo_arrayLikeToSlice() {
|
||||
vm := New()
|
||||
v, err := vm.RunString(`
|
||||
({
|
||||
length: 3,
|
||||
0: 1,
|
||||
1: 2,
|
||||
2: 3
|
||||
});
|
||||
`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var arr []int
|
||||
err = vm.ExportTo(v, &arr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(arr)
|
||||
// Output: [1 2 3]
|
||||
}
|
||||
|
||||
func TestExportArrayToArrayMismatchedLengths(t *testing.T) {
|
||||
vm := New()
|
||||
a := vm.NewArray(1, 2)
|
||||
var a1 [3]int
|
||||
err := vm.ExportTo(a, &a1)
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportIterableToArrayMismatchedLengths(t *testing.T) {
|
||||
vm := New()
|
||||
a, err := vm.RunString(`
|
||||
new Map([[1, true], [2, true]]);
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var a1 [3]interface{}
|
||||
err = vm.ExportTo(a, &a1)
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportArrayLikeToArrayMismatchedLengths(t *testing.T) {
|
||||
vm := New()
|
||||
a, err := vm.RunString(`
|
||||
({
|
||||
length: 2,
|
||||
0: true,
|
||||
1: true
|
||||
});
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var a1 [3]interface{}
|
||||
err = vm.ExportTo(a, &a1)
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetForeignReturnValue(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var array = [1, 2, 3];
|
||||
var arrayTarget = new Proxy(array, {});
|
||||
|
||||
Object.preventExtensions(array);
|
||||
|
||||
!Reflect.set(arrayTarget, "foo", 2);
|
||||
`
|
||||
|
||||
testScript(SCRIPT, valueTrue, t)
|
||||
}
|
||||
|
||||
func TestDefinePropertiesUndefinedVal(t *testing.T) {
|
||||
const SCRIPT = `
|
||||
var target = {};
|
||||
var sym = Symbol();
|
||||
target[sym] = 1;
|
||||
target.foo = 2;
|
||||
target[0] = 3;
|
||||
|
||||
var getOwnKeys = [];
|
||||
var proxy = new Proxy(target, {
|
||||
getOwnPropertyDescriptor: function(_target, key) {
|
||||
getOwnKeys.push(key);
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperties({}, proxy);
|
||||
true;
|
||||
`
|
||||
|
||||
testScript(SCRIPT, valueTrue, t)
|
||||
}
|
||||
|
||||
func ExampleObject_Delete() {
|
||||
vm := New()
|
||||
obj := vm.NewObject()
|
||||
_ = obj.Set("test", true)
|
||||
before := obj.Get("test")
|
||||
_ = obj.Delete("test")
|
||||
after := obj.Get("test")
|
||||
fmt.Printf("before: %v, after: %v", before, after)
|
||||
// Output: before: true, after: <nil>
|
||||
}
|
||||
|
||||
func TestObjectEquality(t *testing.T) {
|
||||
type CustomInt int
|
||||
type S struct {
|
||||
F CustomInt
|
||||
}
|
||||
vm := New()
|
||||
vm.Set("s", S{})
|
||||
// indexOf() and includes() use different equality checks (StrictEquals and SameValueZero respectively),
|
||||
// but for objects they must behave the same. De-referencing s.F creates a new wrapper each time.
|
||||
vm.testScriptWithTestLib(`
|
||||
assert.sameValue([s.F].indexOf(s.F), 0, "indexOf");
|
||||
assert([s.F].includes(s.F));
|
||||
`, _undefined, t)
|
||||
}
|
||||
|
||||
func BenchmarkPut(b *testing.B) {
|
||||
v := &Object{}
|
||||
|
||||
o := &baseObject{
|
||||
val: v,
|
||||
extensible: true,
|
||||
}
|
||||
v.self = o
|
||||
|
||||
o.init()
|
||||
|
||||
var key Value = asciiString("test")
|
||||
var val Value = valueInt(123)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.setOwn(key, val, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPutStr(b *testing.B) {
|
||||
v := &Object{}
|
||||
|
||||
o := &baseObject{
|
||||
val: v,
|
||||
extensible: true,
|
||||
}
|
||||
|
||||
o.init()
|
||||
|
||||
v.self = o
|
||||
|
||||
var val Value = valueInt(123)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
o.setOwnStr("test", val, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGet(b *testing.B) {
|
||||
v := &Object{}
|
||||
|
||||
o := &baseObject{
|
||||
val: v,
|
||||
extensible: true,
|
||||
}
|
||||
|
||||
o.init()
|
||||
|
||||
v.self = o
|
||||
var n Value = asciiString("test")
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.get(n, nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkGetStr(b *testing.B) {
|
||||
v := &Object{}
|
||||
|
||||
o := &baseObject{
|
||||
val: v,
|
||||
extensible: true,
|
||||
}
|
||||
v.self = o
|
||||
|
||||
o.init()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
o.getStr("test", nil)
|
||||
}
|
||||
}
|
||||
|
||||
func __toString(v Value) string {
|
||||
switch v := v.(type) {
|
||||
case asciiString:
|
||||
return string(v)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToString1(b *testing.B) {
|
||||
v := asciiString("test")
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.toString()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToString2(b *testing.B) {
|
||||
v := asciiString("test")
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
__toString(v)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkConv(b *testing.B) {
|
||||
count := int64(0)
|
||||
for i := 0; i < b.N; i++ {
|
||||
count += valueInt(123).ToInteger()
|
||||
}
|
||||
if count == 0 {
|
||||
b.Fatal("zero")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToUTF8String(b *testing.B) {
|
||||
var s String = asciiString("test")
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = s.String()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
var x, y Value
|
||||
x = valueInt(2)
|
||||
y = valueInt(2)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if xi, ok := x.(valueInt); ok {
|
||||
if yi, ok := y.(valueInt); ok {
|
||||
x = xi + yi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddString(b *testing.B) {
|
||||
var x, y Value
|
||||
|
||||
tst := asciiString("22")
|
||||
x = asciiString("2")
|
||||
y = asciiString("2")
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var z Value
|
||||
if xi, ok := x.(String); ok {
|
||||
if yi, ok := y.(String); ok {
|
||||
z = xi.Concat(yi)
|
||||
}
|
||||
}
|
||||
if !z.StrictEquals(tst) {
|
||||
b.Fatalf("Unexpected result %v", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user