265 lines
5.5 KiB
Go
265 lines
5.5 KiB
Go
|
package goja
|
||
|
|
||
|
import (
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
func TestSparseArraySetLengthWithPropItems(t *testing.T) {
|
||
|
const SCRIPT = `
|
||
|
var a = [1,2,3,4];
|
||
|
a[100000] = 5;
|
||
|
var thrown = false;
|
||
|
|
||
|
Object.defineProperty(a, "2", {value: 42, configurable: false, writable: false});
|
||
|
try {
|
||
|
Object.defineProperty(a, "length", {value: 0, writable: false});
|
||
|
} catch (e) {
|
||
|
thrown = e instanceof TypeError;
|
||
|
}
|
||
|
thrown && a.length === 3;
|
||
|
`
|
||
|
|
||
|
testScript(SCRIPT, valueTrue, t)
|
||
|
}
|
||
|
|
||
|
func TestSparseArraySwitch(t *testing.T) {
|
||
|
vm := New()
|
||
|
_, err := vm.RunString(`
|
||
|
var a = [];
|
||
|
a[20470] = 5; // switch to sparse`)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
a := vm.Get("a").(*Object)
|
||
|
if _, ok := a.self.(*sparseArrayObject); !ok {
|
||
|
t.Fatal("1: array is not sparse")
|
||
|
}
|
||
|
_, err = vm.RunString(`
|
||
|
var cutoffIdx = Math.round(20470 - 20470/8);
|
||
|
for (var i = a.length - 1; i >= cutoffIdx; i--) {
|
||
|
a[i] = i;
|
||
|
}
|
||
|
|
||
|
// At this point it will have switched to a normal array
|
||
|
if (a.length != 20471) {
|
||
|
throw new Error("Invalid length: " + a.length);
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < cutoffIdx; i++) {
|
||
|
if (a[i] !== undefined) {
|
||
|
throw new Error("Invalid value at " + i + ": " + a[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var i = cutoffIdx; i < a.length; i++) {
|
||
|
if (a[i] !== i) {
|
||
|
throw new Error("Invalid value at " + i + ": " + a[i]);
|
||
|
}
|
||
|
}`)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if _, ok := a.self.(*arrayObject); !ok {
|
||
|
t.Fatal("2: array is not normal")
|
||
|
}
|
||
|
_, err = vm.RunString(`
|
||
|
// Now try to expand. Should stay a normal array
|
||
|
a[20471] = 20471;
|
||
|
if (a.length != 20472) {
|
||
|
throw new Error("Invalid length: " + a.length);
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < cutoffIdx; i++) {
|
||
|
if (a[i] !== undefined) {
|
||
|
throw new Error("Invalid value at " + i + ": " + a[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var i = cutoffIdx; i < a.length; i++) {
|
||
|
if (a[i] !== i) {
|
||
|
throw new Error("Invalid value at " + i + ": " + a[i]);
|
||
|
}
|
||
|
}`)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if _, ok := a.self.(*arrayObject); !ok {
|
||
|
t.Fatal("3: array is not normal")
|
||
|
}
|
||
|
_, err = vm.RunString(`
|
||
|
// Delete enough elements for it to become sparse again.
|
||
|
var cutoffIdx1 = Math.round(20472 - 20472/10);
|
||
|
for (var i = cutoffIdx; i < cutoffIdx1; i++) {
|
||
|
delete a[i];
|
||
|
}
|
||
|
|
||
|
// This should switch it back to sparse.
|
||
|
a[25590] = 25590;
|
||
|
if (a.length != 25591) {
|
||
|
throw new Error("Invalid length: " + a.length);
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < cutoffIdx1; i++) {
|
||
|
if (a[i] !== undefined) {
|
||
|
throw new Error("Invalid value at " + i + ": " + a[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var i = cutoffIdx1; i < 20472; i++) {
|
||
|
if (a[i] !== i) {
|
||
|
throw new Error("Invalid value at " + i + ": " + a[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var i = 20472; i < 25590; i++) {
|
||
|
if (a[i] !== undefined) {
|
||
|
throw new Error("Invalid value at " + i + ": " + a[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (a[25590] !== 25590) {
|
||
|
throw new Error("Invalid value at 25590: " + a[25590]);
|
||
|
}
|
||
|
`)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if _, ok := a.self.(*sparseArrayObject); !ok {
|
||
|
t.Fatal("4: array is not sparse")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSparseArrayOwnKeys(t *testing.T) {
|
||
|
const SCRIPT = `
|
||
|
var a1 = [];
|
||
|
a1[500000] = 1;
|
||
|
var seen = false;
|
||
|
var count = 0;
|
||
|
var keys = Object.keys(a1);
|
||
|
keys.length === 1 && keys[0] === "500000";
|
||
|
`
|
||
|
|
||
|
testScript(SCRIPT, valueTrue, t)
|
||
|
}
|
||
|
|
||
|
func TestSparseArrayEnumerate(t *testing.T) {
|
||
|
const SCRIPT = `
|
||
|
var a1 = [];
|
||
|
a1[500000] = 1;
|
||
|
var seen = false;
|
||
|
var count = 0;
|
||
|
for (var i in a1) {
|
||
|
if (i === "500000") {
|
||
|
if (seen) {
|
||
|
throw new Error("seen twice");
|
||
|
}
|
||
|
seen = true;
|
||
|
}
|
||
|
count++;
|
||
|
}
|
||
|
seen && count === 1;
|
||
|
`
|
||
|
|
||
|
testScript(SCRIPT, valueTrue, t)
|
||
|
}
|
||
|
|
||
|
func TestArraySparseMaxLength(t *testing.T) {
|
||
|
const SCRIPT = `
|
||
|
var a = [];
|
||
|
a[4294967294]=1;
|
||
|
a.length === 4294967295 && a[4294967294] === 1;
|
||
|
`
|
||
|
|
||
|
testScript(SCRIPT, valueTrue, t)
|
||
|
}
|
||
|
|
||
|
func TestArraySparseExportProps(t *testing.T) {
|
||
|
vm := New()
|
||
|
proto := vm.NewArray()
|
||
|
for _, idx := range []string{"0", "500", "9999", "10001", "20471"} {
|
||
|
err := proto.Set(idx, true)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
arr := vm.NewArray()
|
||
|
err := arr.SetPrototype(proto)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
err = arr.DefineDataProperty("20470", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
err = arr.DefineDataProperty("10000", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
err = arr.Set("length", 20472)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
actual := arr.Export()
|
||
|
if actualArr, ok := actual.([]interface{}); ok {
|
||
|
if len(actualArr) == 20472 {
|
||
|
expectedIdx := map[int]struct{}{
|
||
|
0: {},
|
||
|
500: {},
|
||
|
9999: {},
|
||
|
10000: {},
|
||
|
10001: {},
|
||
|
20470: {},
|
||
|
20471: {},
|
||
|
}
|
||
|
for i, v := range actualArr {
|
||
|
if _, exists := expectedIdx[i]; exists {
|
||
|
if v != true {
|
||
|
t.Fatalf("Expected true at %d, got %v", i, v)
|
||
|
}
|
||
|
} else {
|
||
|
if v != nil {
|
||
|
t.Fatalf("Expected nil at %d, got %v", i, v)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
t.Fatalf("Expected len 20471, actual: %d", len(actualArr))
|
||
|
}
|
||
|
} else {
|
||
|
t.Fatalf("Invalid export type: %T", actual)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSparseArrayExportToSlice(t *testing.T) {
|
||
|
vm := New()
|
||
|
arr := vm.NewArray()
|
||
|
err := arr.Set("20470", 120470)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
err = arr.DefineDataProperty("20471", vm.ToValue(220471), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
var exp []int
|
||
|
err = vm.ExportTo(arr, &exp)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(exp) != 20472 {
|
||
|
t.Fatalf("len: %d", len(exp))
|
||
|
}
|
||
|
if e := exp[20470]; e != 120470 {
|
||
|
t.Fatalf("20470: %d", e)
|
||
|
}
|
||
|
if e := exp[20471]; e != 220471 {
|
||
|
t.Fatalf("20471: %d", e)
|
||
|
}
|
||
|
for i := 0; i < 20470; i++ {
|
||
|
if exp[i] != 0 {
|
||
|
t.Fatalf("at %d: %d", i, exp[i])
|
||
|
}
|
||
|
}
|
||
|
}
|