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

     1  package goja
     2  
     3  import (
     4  	"testing"
     5  )
     6  
     7  func TestSparseArraySetLengthWithPropItems(t *testing.T) {
     8  	const SCRIPT = `
     9  	var a = [1,2,3,4];
    10  	a[100000] = 5;
    11  	var thrown = false;
    12  
    13  	Object.defineProperty(a, "2", {value: 42, configurable: false, writable: false});
    14  	try {
    15  		Object.defineProperty(a, "length", {value: 0, writable: false});
    16  	} catch (e) {
    17  		thrown = e instanceof TypeError;
    18  	}
    19  	thrown && a.length === 3;
    20  	`
    21  
    22  	testScript(SCRIPT, valueTrue, t)
    23  }
    24  
    25  func TestSparseArraySwitch(t *testing.T) {
    26  	vm := New()
    27  	_, err := vm.RunString(`
    28  	var a = [];
    29  	a[20470] = 5; // switch to sparse`)
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	a := vm.Get("a").(*Object)
    34  	if _, ok := a.self.(*sparseArrayObject); !ok {
    35  		t.Fatal("1: array is not sparse")
    36  	}
    37  	_, err = vm.RunString(`
    38  	var cutoffIdx = Math.round(20470 - 20470/8);
    39  	for (var i = a.length - 1; i >= cutoffIdx; i--) {
    40  		a[i] = i;
    41  	}
    42  
    43  	// At this point it will have switched to a normal array
    44  	if (a.length != 20471) {
    45  		throw new Error("Invalid length: " + a.length);
    46  	}
    47  
    48  	for (var i = 0; i < cutoffIdx; i++) {
    49  		if (a[i] !== undefined) {
    50  			throw new Error("Invalid value at " + i + ": " + a[i]);
    51  		}
    52  	}
    53  
    54  	for (var i = cutoffIdx; i < a.length; i++) {
    55  		if (a[i] !== i) {
    56  			throw new Error("Invalid value at " + i + ": " + a[i]);
    57  		}
    58  	}`)
    59  	if err != nil {
    60  		t.Fatal(err)
    61  	}
    62  	if _, ok := a.self.(*arrayObject); !ok {
    63  		t.Fatal("2: array is not normal")
    64  	}
    65  	_, err = vm.RunString(`
    66  	// Now try to expand. Should stay a normal array
    67  	a[20471] = 20471;
    68  	if (a.length != 20472) {
    69  		throw new Error("Invalid length: " + a.length);
    70  	}
    71  
    72  	for (var i = 0; i < cutoffIdx; i++) {
    73  		if (a[i] !== undefined) {
    74  			throw new Error("Invalid value at " + i + ": " + a[i]);
    75  		}
    76  	}
    77  
    78  	for (var i = cutoffIdx; i < a.length; i++) {
    79  		if (a[i] !== i) {
    80  			throw new Error("Invalid value at " + i + ": " + a[i]);
    81  		}
    82  	}`)
    83  	if err != nil {
    84  		t.Fatal(err)
    85  	}
    86  	if _, ok := a.self.(*arrayObject); !ok {
    87  		t.Fatal("3: array is not normal")
    88  	}
    89  	_, err = vm.RunString(`
    90  	// Delete enough elements for it to become sparse again.
    91  	var cutoffIdx1 = Math.round(20472 - 20472/10);
    92  	for (var i = cutoffIdx; i < cutoffIdx1; i++) {
    93  		delete a[i];
    94  	}
    95  
    96  	// This should switch it back to sparse.
    97  	a[25590] = 25590;
    98  	if (a.length != 25591) {
    99  		throw new Error("Invalid length: " + a.length);
   100  	}
   101  
   102  	for (var i = 0; i < cutoffIdx1; i++) {
   103  		if (a[i] !== undefined) {
   104  			throw new Error("Invalid value at " + i + ": " + a[i]);
   105  		}
   106  	}
   107  
   108  	for (var i = cutoffIdx1; i < 20472; i++) {
   109  		if (a[i] !== i) {
   110  			throw new Error("Invalid value at " + i + ": " + a[i]);
   111  		}
   112  	}
   113  
   114  	for (var i = 20472; i < 25590; i++) {
   115  		if (a[i] !== undefined) {
   116  			throw new Error("Invalid value at " + i + ": " + a[i]);
   117  		}
   118  	}
   119  
   120  	if (a[25590] !== 25590) {
   121  		throw new Error("Invalid value at 25590: " + a[25590]);
   122  	}
   123  	`)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	if _, ok := a.self.(*sparseArrayObject); !ok {
   128  		t.Fatal("4: array is not sparse")
   129  	}
   130  }
   131  
   132  func TestSparseArrayOwnKeys(t *testing.T) {
   133  	const SCRIPT = `
   134  	var a1 = [];
   135  	a1[500000] = 1;
   136  	var seen = false;
   137  	var count = 0;
   138  	var keys = Object.keys(a1);
   139  	keys.length === 1 && keys[0] === "500000"; 
   140  	`
   141  
   142  	testScript(SCRIPT, valueTrue, t)
   143  }
   144  
   145  func TestSparseArrayEnumerate(t *testing.T) {
   146  	const SCRIPT = `
   147  	var a1 = [];
   148  	a1[500000] = 1;
   149  	var seen = false;
   150  	var count = 0;
   151  	for (var i in a1) {
   152  		if (i === "500000") {
   153  			if (seen) {
   154  				throw new Error("seen twice");
   155  			}
   156  			seen = true;
   157  		}
   158  		count++;
   159  	}
   160  	seen && count === 1;
   161  	`
   162  
   163  	testScript(SCRIPT, valueTrue, t)
   164  }
   165  
   166  func TestArraySparseMaxLength(t *testing.T) {
   167  	const SCRIPT = `
   168  	var a = [];
   169  	a[4294967294]=1;
   170  	a.length === 4294967295 && a[4294967294] === 1;
   171  	`
   172  
   173  	testScript(SCRIPT, valueTrue, t)
   174  }
   175  
   176  func TestArraySparseExportProps(t *testing.T) {
   177  	vm := New()
   178  	proto := vm.NewArray()
   179  	for _, idx := range []string{"0", "500", "9999", "10001", "20471"} {
   180  		err := proto.Set(idx, true)
   181  		if err != nil {
   182  			t.Fatal(err)
   183  		}
   184  	}
   185  
   186  	arr := vm.NewArray()
   187  	err := arr.SetPrototype(proto)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	err = arr.DefineDataProperty("20470", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  	err = arr.DefineDataProperty("10000", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  	err = arr.Set("length", 20472)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	actual := arr.Export()
   204  	if actualArr, ok := actual.([]interface{}); ok {
   205  		if len(actualArr) == 20472 {
   206  			expectedIdx := map[int]struct{}{
   207  				0:     {},
   208  				500:   {},
   209  				9999:  {},
   210  				10000: {},
   211  				10001: {},
   212  				20470: {},
   213  				20471: {},
   214  			}
   215  			for i, v := range actualArr {
   216  				if _, exists := expectedIdx[i]; exists {
   217  					if v != true {
   218  						t.Fatalf("Expected true at %d, got %v", i, v)
   219  					}
   220  				} else {
   221  					if v != nil {
   222  						t.Fatalf("Expected nil at %d, got %v", i, v)
   223  					}
   224  				}
   225  			}
   226  		} else {
   227  			t.Fatalf("Expected len 20471, actual: %d", len(actualArr))
   228  		}
   229  	} else {
   230  		t.Fatalf("Invalid export type: %T", actual)
   231  	}
   232  }
   233  
   234  func TestSparseArrayExportToSlice(t *testing.T) {
   235  	vm := New()
   236  	arr := vm.NewArray()
   237  	err := arr.Set("20470", 120470)
   238  	if err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	err = arr.DefineDataProperty("20471", vm.ToValue(220471), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	var exp []int
   246  	err = vm.ExportTo(arr, &exp)
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	if len(exp) != 20472 {
   251  		t.Fatalf("len: %d", len(exp))
   252  	}
   253  	if e := exp[20470]; e != 120470 {
   254  		t.Fatalf("20470: %d", e)
   255  	}
   256  	if e := exp[20471]; e != 220471 {
   257  		t.Fatalf("20471: %d", e)
   258  	}
   259  	for i := 0; i < 20470; i++ {
   260  		if exp[i] != 0 {
   261  			t.Fatalf("at %d: %d", i, exp[i])
   262  		}
   263  	}
   264  }