1 /* 2 * Copyright (c) 2014, 2018, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime.arrays; 27 28 import java.util.Iterator; 29 import java.util.List; 30 import java.util.SortedMap; 31 import java.util.TreeMap; 32 import jdk.nashorn.internal.runtime.JSType; 33 import jdk.nashorn.internal.runtime.ScriptRuntime; 34 35 /** 36 * Filter to use for ArrayData where the length is not writable. 37 * The default behavior is just to ignore {@link ArrayData#setLength} 38 */ 39 final class LengthNotWritableFilter extends ArrayFilter { 40 private final SortedMap<Long, Object> extraElements; //elements with index >= length 41 42 /** 43 * Constructor 44 * @param underlying array 45 */ 46 LengthNotWritableFilter(final ArrayData underlying) { 47 this(underlying, new TreeMap<Long, Object>()); 48 } 49 50 private LengthNotWritableFilter(final ArrayData underlying, final SortedMap<Long, Object> extraElements) { 51 super(underlying); 52 this.extraElements = extraElements; 53 } 54 55 @Override 56 public ArrayData copy() { 57 return new LengthNotWritableFilter(underlying.copy(), new TreeMap<>(extraElements)); 58 } 59 60 @Override 61 public boolean has(final int index) { 62 return super.has(index) || extraElements.containsKey((long)index); 63 } 64 65 /** 66 * Set the length of the data array 67 * 68 * @param length the new length for the data array 69 */ 70 @Override 71 public void setLength(final long length) { 72 //empty - setting length for a LengthNotWritableFilter is always a nop 73 } 74 75 @Override 76 public ArrayData ensure(final long index) { 77 return this; 78 } 79 80 @Override 81 public ArrayData slice(final long from, final long to) { 82 //return array[from...to), or array[from...length] if undefined, in this case not as we are an ArrayData 83 return new LengthNotWritableFilter(underlying.slice(from, to), extraElements.subMap(from, to)); 84 } 85 86 private boolean checkAdd(final long index, final Object value) { 87 if (index >= length()) { 88 extraElements.put(index, value); 89 return true; 90 } 91 return false; 92 } 93 94 private Object get(final long index) { 95 final Object obj = extraElements.get(index); 96 if (obj == null) { 97 return ScriptRuntime.UNDEFINED; 98 } 99 return obj; 100 } 101 102 @Override 103 public int getInt(final int index) { 104 if (index >= length()) { 105 return JSType.toInt32(get(index)); 106 } 107 return underlying.getInt(index); 108 } 109 110 @Override 111 public int getIntOptimistic(final int index, final int programPoint) { 112 if (index >= length()) { 113 return JSType.toInt32Optimistic(get(index), programPoint); 114 } 115 return underlying.getIntOptimistic(index, programPoint); 116 } 117 118 @Override 119 public double getDouble(final int index) { 120 if (index >= length()) { 121 return JSType.toNumber(get(index)); 122 } 123 return underlying.getDouble(index); 124 } 125 126 @Override 127 public double getDoubleOptimistic(final int index, final int programPoint) { 128 if (index >= length()) { 129 return JSType.toNumberOptimistic(get(index), programPoint); 130 } 131 return underlying.getDoubleOptimistic(index, programPoint); 132 } 133 134 @Override 135 public Object getObject(final int index) { 136 if (index >= length()) { 137 return get(index); 138 } 139 return underlying.getObject(index); 140 } 141 142 @Override 143 public ArrayData set(final int index, final Object value, final boolean strict) { 144 if (checkAdd(index, value)) { 145 return this; 146 } 147 underlying = underlying.set(index, value, strict); 148 return this; 149 } 150 151 @Override 152 public ArrayData set(final int index, final int value, final boolean strict) { 153 if (checkAdd(index, value)) { 154 return this; 155 } 156 underlying = underlying.set(index, value, strict); 157 return this; 158 } 159 160 @Override 161 public ArrayData set(final int index, final double value, final boolean strict) { 162 if (checkAdd(index, value)) { 163 return this; 164 } 165 underlying = underlying.set(index, value, strict); 166 return this; 167 } 168 169 @Override 170 public ArrayData delete(final int index) { 171 extraElements.remove(ArrayIndex.toLongIndex(index)); 172 underlying = underlying.delete(index); 173 return this; 174 } 175 176 @Override 177 public ArrayData delete(final long fromIndex, final long toIndex) { 178 for (final Iterator<Long> iter = extraElements.keySet().iterator(); iter.hasNext();) { 179 final long next = iter.next(); 180 if (next >= fromIndex && next <= toIndex) { 181 iter.remove(); 182 } 183 if (next > toIndex) { //ordering guaranteed because TreeSet 184 break; 185 } 186 } 187 underlying = underlying.delete(fromIndex, toIndex); 188 return this; 189 } 190 191 @Override 192 public Iterator<Long> indexIterator() { 193 final List<Long> keys = computeIteratorKeys(); 194 keys.addAll(extraElements.keySet()); //even if they are outside length this is fine 195 return keys.iterator(); 196 } 197 198 }