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 double getDouble(final int index) { 95 if (index >= length()) { 96 return JSType.toNumber(get(index)); 97 } 98 return underlying.getDouble(index); 99 } 100 101 @Override 102 public double getDoubleOptimistic(final int index, final int programPoint) { 103 if (index >= length()) { 104 return JSType.toNumberOptimistic(get(index), programPoint); 105 } 106 return underlying.getDoubleOptimistic(index, programPoint); 107 } 108 109 @Override 110 public Object getObject(final int index) { 111 if (index >= length()) { 112 return get(index); 113 } 114 return underlying.getObject(index); 115 } 116 117 @Override 118 public ArrayData set(final int index, final Object value, final boolean strict) { 119 if (checkAdd(index, value)) { 120 return this; 121 } 122 underlying = underlying.set(index, value, strict); 123 return this; 124 } 125 126 @Override 127 public ArrayData set(final int index, final int value, final boolean strict) { 128 if (checkAdd(index, value)) { 129 return this; 130 } 131 underlying = underlying.set(index, value, strict); 132 return this; 133 } 134 135 @Override 136 public ArrayData set(final int index, final double value, final boolean strict) { 137 if (checkAdd(index, value)) { 138 return this; 139 } 140 underlying = underlying.set(index, value, strict); 141 return this; 142 } 143 144 @Override 145 public ArrayData delete(final int index) { 146 extraElements.remove(index); 147 underlying = underlying.delete(index); 148 return this; 149 } 150 151 @Override 152 public ArrayData delete(final long fromIndex, final long toIndex) { 153 for (final Iterator<Long> iter = extraElements.keySet().iterator(); iter.hasNext();) { 154 final long next = iter.next(); 155 if (next >= fromIndex && next <= toIndex) { 156 iter.remove(); 157 } 158 if (next > toIndex) { //ordering guaranteed because TreeSet 159 break; 160 } 161 } 162 underlying = underlying.delete(fromIndex, toIndex); 163 return this; 164 } 165 166 @Override 167 public Iterator<Long> indexIterator() { 168 final List<Long> keys = computeIteratorKeys(); 169 keys.addAll(extraElements.keySet()); //even if they are outside length this is fine 170 return keys.iterator(); 171 } 172 173 }