1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * JDK-8035312 push to frozen array must not increase length property
  26  *
  27  * @test
  28  * @run
  29  * @fork
  30  * @option -Dnashorn.debug=true
  31  */
  32 
  33 function printArrayDataClass(x) {
  34     if (typeof Debug !== 'undefined') {
  35         print(Debug.getArrayDataClass(x));
  36     }
  37 }
  38 
  39 function gpush(x, elem) {
  40     try {
  41         print("Pushing " + elem + " to " + x);
  42         x.push(elem);
  43     } catch (e) {
  44         print("caught error" + e);
  45     }
  46     print("\tarray is now [" + x + "] length is = " + x.length);
  47     print();
  48     printArrayDataClass(x);
  49 }
  50 
  51 function gpop(x) {
  52     try {
  53         print("Popping from " + x);
  54         x.pop();
  55     } catch (e) {
  56         if (!(e instanceof TypeError)) {
  57             print("e of wrong type " + e);
  58         }
  59     }
  60     print("\tarray is now [" + x + "] length is = " + x.length);
  61     print();
  62     printArrayDataClass(x);
  63 }
  64 
  65 function checkArray(x) {
  66     print();
  67     print(">>> Push test");
  68 
  69     var olen = x.length;
  70     gpush(x, 0);
  71 
  72     print("x.length === " + x.length + " (should be " + olen + ")");
  73     print("x[3] === " + x[3] + " (should be 0)");
  74     print("x[4] === " + x[4] + " (should be undefined)");
  75 
  76     print();
  77     print(">>> Pop test");
  78     gpop(x);
  79     gpop(x);
  80     print("x.length === " + x.length + " (should be " + olen + ")");
  81     print("x === " + x);
  82 
  83     for (var i = 0 ; i < 5; i++) {
  84         gpop(x);
  85     }
  86 
  87     print("x.length === " + x.length + " (should be " + olen + ")");
  88     print("x === " + x);
  89 }
  90 
  91 print("*** Freezing");
  92 var frozen = [1,2,3];
  93 Object.freeze(frozen);
  94 checkArray(frozen);
  95 printArrayDataClass(frozen);
  96 
  97 //so far so good
  98 
  99 print();
 100 print("*** Other length not writable issues");
 101 var lengthNotWritable = [1,2,3];
 102 Object.defineProperty(lengthNotWritable, "length", { writable: false });
 103 checkArray(lengthNotWritable);
 104 printArrayDataClass(lengthNotWritable);
 105 
 106 function set(array, from, to, stride) {
 107     //add three elements
 108     for (var i = from; i < to; i+=stride) {
 109         try {
 110             print("Writing " + i);
 111             array[i] = i;
 112             printArrayDataClass(array);
 113         } catch (e) {
 114             print(e instanceof TypeError);
 115         }
 116     }
 117 }
 118 
 119 //define empty array with non writable length
 120 var arr = [1];
 121 Object.defineProperty(arr, "length", { writable: false });
 122 
 123 var olen2 = arr.length;
 124 
 125 set(arr, 0, 3, 1);
 126 
 127 if (arr.length != olen2) {
 128     throw new ("error: " +  arr.length + " != " + olen2);
 129 }
 130 
 131 print();
 132 print("array writing 0-3, with 1 stride, array = " + arr);
 133 print("length = " + arr.length + ", but elements are: " + arr[0] + " " + arr[1] + " " + arr[2]);
 134 print();
 135 
 136 //do the same but sparse/deleted range
 137 var arr2 = [1];
 138 Object.defineProperty(arr2, "length", { writable: false });
 139 
 140 print("initial length = " + arr2.length);
 141 var olen3 = arr2.length;
 142 
 143 set(arr2, 0, 30, 3);
 144 
 145 if (arr2.length != olen3) {
 146     throw new ("error: " +  arr2.length + " != " + olen3);
 147 }
 148 
 149 print();
 150 var larger = 20;
 151 print("array writing 0-" + larger + ", with 3 stride, array = " + arr2);
 152 print("length = " + arr2.length + ", but elements are: " + arr2[0] + " " + arr2[1] + " " + arr2[2]);
 153 
 154 for (var i = 0; i < larger; i++) {
 155     if (arr2[i] === undefined) {
 156         continue;
 157     }
 158     print(arr2[i] + " has length " + arr2.length);
 159 }
 160 
 161 print();
 162 var elem = 0x7fffffff - 10;
 163 printArrayDataClass(arr2);
 164 print("adding a new element high up in the array");
 165 print("length before element was added " + arr2.length);
 166 print("putting sparse at " + elem);
 167 arr2[elem] = "sparse";
 168 print("length after element was added " + arr2.length + " should be the same");
 169 printArrayDataClass(arr2);
 170 
 171 print();
 172 print("Printing arr2 - this will fail if length is > 28 and it is " + arr2.length);
 173 print("arr2 = [" + arr2 + "]");
 174 print("new length that should not be writable = " + arr2.length);
 175 print(arr2[elem] === "sparse");
 176 print(arr2[elem]);
 177 for (var i = 0; i < larger; i++) {
 178     print(arr2[i]);
 179 }
 180 for (var key in arr2) {
 181     print(key + ":" + arr2[key]);
 182 }
 183 
 184 //issues reported by sundar - generic setter doesn't go through push/pop bulkable
 185 
 186 function sundarExample2(arr, _writable) {
 187     print("Checking if push works for bulkable non bulkable arrays - Setting length property not allowed");
 188     arr[0] = "bar";
 189     print(arr.length + " should be 1"); // should be 1
 190     print(arr[0] + " should be bar");
 191     print("["+ arr + "] should be [bar]");
 192 
 193     //    Object.defineProperty(arr, "length", { configurable: _writable });
 194     Object.defineProperty(arr, "length", { writable: _writable });
 195     arr[1] = "baz";
 196 
 197     if (_writable) {
 198         print(arr.length + " should be 2");
 199         print(arr[0] + " should be bar");
 200         print(arr[1] + " should be baz");
 201         print("["+ arr + "] should be [bar,baz]");
 202     } else {
 203         print(arr.length + " should STILL be 1");
 204         print(arr[0] + " should be bar");
 205         print(arr[1] + " should be baz");
 206         print("["+ arr + "] should be [bar]");
 207     }
 208 }
 209 
 210 var newArr1 = [];
 211 sundarExample2(newArr1, false);
 212 print();
 213 try {
 214     sundarExample2(newArr1, true);
 215     print("should not get here");
 216 } catch (e) {
 217     if (!(e instanceof TypeError)) {
 218         print("Wrong exception");
 219     }
 220     print("got TypeError when redefining length, as expected")
 221 }
 222 print();
 223 
 224 sundarExample2([], true);
 225 print("Done");