1 package jdk.nashorn.internal.runtime.arrays; 2 3 import java.util.Iterator; 4 import java.util.List; 5 import java.util.SortedMap; 6 import java.util.TreeMap; 7 import jdk.nashorn.internal.runtime.JSType; 8 import jdk.nashorn.internal.runtime.ScriptRuntime; 9 10 /** 11 * Filter to use for ArrayData where the length is not writable. 12 * The default behavior is just to ignore {@link ArrayData#setLength} 13 */ 14 final class LengthNotWritableFilter extends ArrayFilter { 15 private final SortedMap<Long, Object> extraElements; //elements with index >= length 16 17 /** 18 * Constructor 19 * @param underlying array 20 */ 21 LengthNotWritableFilter(final ArrayData underlying) { 22 this(underlying, new TreeMap<Long, Object>()); 23 } 24 25 private LengthNotWritableFilter(final ArrayData underlying, final SortedMap<Long, Object> extraElements) { 26 super(underlying); 27 this.extraElements = extraElements; 28 } 29 30 @Override 31 public ArrayData copy() { 32 return new LengthNotWritableFilter(underlying.copy(), new TreeMap<>(extraElements)); 33 } 34 35 @Override 36 public boolean has(final int index) { 37 return super.has(index) || extraElements.containsKey((long)index); 38 } 39 40 /** 41 * Set the length of the data array 42 * 43 * @param length the new length for the data array 44 */ 45 @Override 46 public void setLength(final long length) { 47 //empty - setting length for a LengthNotWritableFilter is always a nop 48 } 49 50 @Override 51 public ArrayData ensure(final long index) { 52 return this; 53 } 54 55 @Override 56 public ArrayData slice(final long from, final long to) { 57 //return array[from...to), or array[from...length] if undefined, in this case not as we are an ArrayData 58 return new LengthNotWritableFilter(underlying.slice(from, to), extraElements.subMap(from, to)); 59 } 60 61 private boolean checkAdd(final long index, final Object value) { 62 if (index >= length()) { 63 extraElements.put(index, value); 64 return true; 65 } 66 return false; 67 } 68 69 private Object get(final long index) { 70 final Object obj = extraElements.get(index); 71 if (obj == null) { 72 return ScriptRuntime.UNDEFINED; 73 } 74 return obj; 75 } 76 77 @Override 78 public int getInt(final int index) { 79 if (index >= length()) { 80 return JSType.toInt32(get(index)); 81 } 82 return underlying.getInt(index); 83 } 84 85 @Override 86 public int getIntOptimistic(final int index, final int programPoint) { 87 if (index >= length()) { 88 return JSType.toInt32Optimistic(get(index), programPoint); 89 } 90 return underlying.getIntOptimistic(index, programPoint); 91 } 92 93 @Override 94 public long getLong(final int index) { 95 if (index >= length()) { 96 return JSType.toLong(get(index)); 97 } 98 return underlying.getLong(index); 99 } 100 101 @Override 102 public long getLongOptimistic(final int index, final int programPoint) { 103 if (index >= length()) { 104 return JSType.toLongOptimistic(get(index), programPoint); 105 } 106 return underlying.getLongOptimistic(index, programPoint); 107 } 108 109 @Override 110 public double getDouble(final int index) { 111 if (index >= length()) { 112 return JSType.toNumber(get(index)); 113 } 114 return underlying.getDouble(index); 115 } 116 117 @Override 118 public double getDoubleOptimistic(final int index, final int programPoint) { 119 if (index >= length()) { 120 return JSType.toNumberOptimistic(get(index), programPoint); 121 } 122 return underlying.getDoubleOptimistic(index, programPoint); 123 } 124 125 @Override 126 public Object getObject(final int index) { 127 if (index >= length()) { 128 return get(index); 129 } 130 return underlying.getObject(index); 131 } 132 133 @Override 134 public ArrayData set(final int index, final Object value, final boolean strict) { 135 if (checkAdd(index, value)) { 136 return this; 137 } 138 underlying = underlying.set(index, value, strict); 139 return this; 140 } 141 142 @Override 143 public ArrayData set(final int index, final int 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 long 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(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 }