# HG changeset patch # User lagergren # Date 1415797921 -3600 # Node ID 3dbb4c9ff43c5581bb9bdb603cec40f0d05976b0 # Parent 56c0d55ea56275b35190d38c9a5ad0e12f3a2221 8035312: Various array and ScriptObject length issues for non writable length fields Reviewed-by: hannesw, attila diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/objects/NativeArray.java --- a/src/jdk/nashorn/internal/objects/NativeArray.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/objects/NativeArray.java Wed Nov 12 14:12:01 2014 +0100 @@ -33,9 +33,7 @@ import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator; import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT; - import java.lang.invoke.MethodHandle; -import java.lang.invoke.SwitchPoint; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -93,17 +91,10 @@ private static final Object CALL_CMP = new Object(); private static final Object TO_LOCALE_STRING = new Object(); - private SwitchPoint lengthMadeNotWritableSwitchPoint; private PushLinkLogic pushLinkLogic; private PopLinkLogic popLinkLogic; private ConcatLinkLogic concatLinkLogic; - /** - * Index for the modification SwitchPoint that triggers when length - * becomes not writable - */ - private static final int LENGTH_NOT_WRITABLE_SWITCHPOINT = 0; - /* * Constructors. */ @@ -271,12 +262,83 @@ @Override public Object getLength() { final long length = JSType.toUint32(getArray().length()); - if(length < Integer.MAX_VALUE) { + if (length < Integer.MAX_VALUE) { return (int)length; } return length; } + private boolean defineLength(final long oldLen, final PropertyDescriptor oldLenDesc, final PropertyDescriptor desc, final boolean reject) { + // Step 3a + if (!desc.has(VALUE)) { + return super.defineOwnProperty("length", desc, reject); + } + + // Step 3b + final PropertyDescriptor newLenDesc = desc; + + // Step 3c and 3d - get new length and convert to long + final long newLen = NativeArray.validLength(newLenDesc.getValue(), true); + + // Step 3e + newLenDesc.setValue(newLen); + + // Step 3f + // increasing array length - just need to set new length value (and attributes if any) and return + if (newLen >= oldLen) { + return super.defineOwnProperty("length", newLenDesc, reject); + } + + // Step 3g + if (!oldLenDesc.isWritable()) { + if (reject) { + throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); + } + return false; + } + + // Step 3h and 3i + final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable(); + if (!newWritable) { + newLenDesc.setWritable(true); + } + + // Step 3j and 3k + final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject); + if (!succeeded) { + return false; + } + + // Step 3l + // make sure that length is set till the point we can delete the old elements + long o = oldLen; + while (newLen < o) { + o--; + final boolean deleteSucceeded = delete(o, false); + if (!deleteSucceeded) { + newLenDesc.setValue(o + 1); + if (!newWritable) { + newLenDesc.setWritable(false); + } + super.defineOwnProperty("length", newLenDesc, false); + if (reject) { + throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); + } + return false; + } + } + + // Step 3m + if (!newWritable) { + // make 'length' property not writable + final ScriptObject newDesc = Global.newEmptyInstance(); + newDesc.set(WRITABLE, false, 0); + return super.defineOwnProperty("length", newDesc, false); + } + + return true; + } + /** * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw ) */ @@ -290,82 +352,16 @@ // Step 2 // get old length and convert to long - long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true); + final long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true); // Step 3 if ("length".equals(key)) { // check for length being made non-writable + final boolean result = defineLength(oldLen, oldLenDesc, desc, reject); if (desc.has(WRITABLE) && !desc.isWritable()) { setIsLengthNotWritable(); } - - // Step 3a - if (!desc.has(VALUE)) { - return super.defineOwnProperty("length", desc, reject); - } - - // Step 3b - final PropertyDescriptor newLenDesc = desc; - - // Step 3c and 3d - get new length and convert to long - final long newLen = NativeArray.validLength(newLenDesc.getValue(), true); - - // Step 3e - newLenDesc.setValue(newLen); - - // Step 3f - // increasing array length - just need to set new length value (and attributes if any) and return - if (newLen >= oldLen) { - return super.defineOwnProperty("length", newLenDesc, reject); - } - - // Step 3g - if (!oldLenDesc.isWritable()) { - if (reject) { - throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); - } - return false; - } - - // Step 3h and 3i - final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable(); - if (!newWritable) { - newLenDesc.setWritable(true); - } - - // Step 3j and 3k - final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject); - if (!succeeded) { - return false; - } - - // Step 3l - // make sure that length is set till the point we can delete the old elements - while (newLen < oldLen) { - oldLen--; - final boolean deleteSucceeded = delete(oldLen, false); - if (!deleteSucceeded) { - newLenDesc.setValue(oldLen + 1); - if (!newWritable) { - newLenDesc.setWritable(false); - } - super.defineOwnProperty("length", newLenDesc, false); - if (reject) { - throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this)); - } - return false; - } - } - - // Step 3m - if (!newWritable) { - // make 'length' property not writable - final ScriptObject newDesc = Global.newEmptyInstance(); - newDesc.set(WRITABLE, false, 0); - return super.defineOwnProperty("length", newDesc, false); - } - - return true; + return result; } // Step 4a @@ -441,23 +437,7 @@ @Override public void setIsLengthNotWritable() { super.setIsLengthNotWritable(); - /* - * Switchpoints are created lazily. If we link any push or pop site, - * we need to create the "length made not writable" switchpoint, if it - * doesn't exist. - * - * If the switchpoint already exists, we will find it here, and invalidate - * it, invalidating all previous callsites that use it. - * - * If the switchpoint doesn't exist, no push/pop has been linked so far, - * because that would create it too. We invalidate it immediately and the - * check link logic for all future callsites will fail immediately at link - * time - */ - if (lengthMadeNotWritableSwitchPoint == null) { - lengthMadeNotWritableSwitchPoint = new SwitchPoint(); - } - SwitchPoint.invalidateAll(new SwitchPoint[] { lengthMadeNotWritableSwitchPoint }); + setArray(ArrayData.setIsLengthNotWritable(getArray())); } /** @@ -494,7 +474,7 @@ @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) public static void length(final Object self, final Object length) { if (isArray(self)) { - ((ScriptObject) self).setLength(validLength(length, true)); + ((ScriptObject)self).setLength(validLength(length, true)); } } @@ -1306,10 +1286,13 @@ // Get only non-missing elements. Missing elements go at the end // of the sorted array. So, just don't copy these to sort input. final ArrayList src = new ArrayList<>(); - for (long i = 0; i < len; i = array.nextIndex(i)) { - if (array.has((int) i)) { - src.add(array.getObject((int) i)); + + for (final Iterator iter = array.indexIterator(); iter.hasNext(); ) { + final long index = iter.next(); + if (index >= len) { + break; } + src.add(array.getObject((int)index)); } final Object[] sorted = sort(src.toArray(), comparefn); @@ -1767,11 +1750,11 @@ @Override public SpecializedFunction.LinkLogic getLinkLogic(final Class clazz) { if (clazz == PushLinkLogic.class) { - return pushLinkLogic == null ? new PushLinkLogic(this) : pushLinkLogic; + return pushLinkLogic == null ? new PushLinkLogic() : pushLinkLogic; } else if (clazz == PopLinkLogic.class) { - return popLinkLogic == null ? new PopLinkLogic(this) : pushLinkLogic; + return popLinkLogic == null ? new PopLinkLogic() : pushLinkLogic; } else if (clazz == ConcatLinkLogic.class) { - return concatLinkLogic == null ? new ConcatLinkLogic(this) : concatLinkLogic; + return concatLinkLogic == null ? new ConcatLinkLogic() : concatLinkLogic; } return null; } @@ -1787,21 +1770,7 @@ * modification switchpoint which is touched when length is written. */ private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic { - private final NativeArray array; - - protected ArrayLinkLogic(final NativeArray array) { - this.array = array; - } - - private SwitchPoint getSwitchPoint() { - return array.lengthMadeNotWritableSwitchPoint; - } - - private SwitchPoint newSwitchPoint() { - assert array.lengthMadeNotWritableSwitchPoint == null; - final SwitchPoint sp = new SwitchPoint(); - array.lengthMadeNotWritableSwitchPoint = sp; - return sp; + protected ArrayLinkLogic() { } protected static ContinuousArrayData getContinuousArrayData(final Object self) { @@ -1822,69 +1791,12 @@ public Class getRelinkException() { return ClassCastException.class; } - - @Override - public boolean hasModificationSwitchPoints() { - return getSwitchPoint() != null; - } - - @Override - public boolean hasModificationSwitchPoint(final int index) { - assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; - return hasModificationSwitchPoints(); - } - - @Override - public SwitchPoint getOrCreateModificationSwitchPoint(final int index) { - assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; - SwitchPoint sp = getSwitchPoint(); - if (sp == null) { - sp = newSwitchPoint(); - } - return sp; - } - - @Override - public SwitchPoint[] getOrCreateModificationSwitchPoints() { - return new SwitchPoint[] { getOrCreateModificationSwitchPoint(LENGTH_NOT_WRITABLE_SWITCHPOINT) }; - } - - @Override - public void invalidateModificationSwitchPoint(final int index) { - assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; - invalidateModificationSwitchPoints(); - } - - @Override - public void invalidateModificationSwitchPoints() { - final SwitchPoint sp = getSwitchPoint(); - assert sp != null : "trying to invalidate non-existant modified SwitchPoint"; - if (!sp.hasBeenInvalidated()) { - SwitchPoint.invalidateAll(new SwitchPoint[] { sp }); - } - } - - @Override - public boolean hasInvalidatedModificationSwitchPoint(final int index) { - assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT; - return hasInvalidatedModificationSwitchPoints(); - } - - @Override - public boolean hasInvalidatedModificationSwitchPoints() { - final SwitchPoint sp = getSwitchPoint(); - return sp != null && !sp.hasBeenInvalidated(); - } } /** * This is linker logic for optimistic concatenations */ private static final class ConcatLinkLogic extends ArrayLinkLogic { - private ConcatLinkLogic(final NativeArray array) { - super(array); - } - @Override public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { final Object[] args = request.getArguments(); @@ -1915,10 +1827,6 @@ * This is linker logic for optimistic pushes */ private static final class PushLinkLogic extends ArrayLinkLogic { - private PushLinkLogic(final NativeArray array) { - super(array); - } - @Override public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { return getContinuousArrayData(self) != null; @@ -1929,10 +1837,6 @@ * This is linker logic for optimistic pops */ private static final class PopLinkLogic extends ArrayLinkLogic { - private PopLinkLogic(final NativeArray array) { - super(array); - } - /** * We need to check if we are dealing with a continuous non empty array data here, * as pop with a primitive return value returns undefined for arrays with length 0 diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/objects/NativeDebug.java --- a/src/jdk/nashorn/internal/objects/NativeDebug.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/objects/NativeDebug.java Wed Nov 12 14:12:01 2014 +0100 @@ -39,6 +39,7 @@ import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.events.RuntimeEvent; import jdk.nashorn.internal.runtime.linker.LinkerCallSite; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; @@ -66,6 +67,36 @@ } /** + * Return the ArrayData class for this ScriptObject + * @param self self + * @param obj script object to check + * @return ArrayData class, or undefined if no ArrayData is present + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) + public static Object getArrayDataClass(final Object self, final Object obj) { + try { + return ((ScriptObject)obj).getArray().getClass(); + } catch (final ClassCastException e) { + return ScriptRuntime.UNDEFINED; + } + } + + /** + * Return the ArrayData for this ScriptObject + * @param self self + * @param obj script object to check + * @return ArrayData, ArrayDatas have toString methods, return Undefined if data missing + */ + @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) + public static Object getArrayData(final Object self, final Object obj) { + try { + return ((ScriptObject)obj).getArray(); + } catch (final ClassCastException e) { + return ScriptRuntime.UNDEFINED; + } + } + + /** * Nashorn extension: get context, context utility * * @param self self reference diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java --- a/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java Wed Nov 12 14:12:01 2014 +0100 @@ -30,7 +30,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.invoke.MethodHandle; -import java.lang.invoke.SwitchPoint; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.runtime.ScriptFunction; @@ -62,10 +61,6 @@ */ public static final LinkLogic EMPTY_INSTANCE = new Empty(); - private static final SwitchPoint[] INVALIDATED_SWITCHPOINTS = new SwitchPoint[0]; - - private SwitchPoint[] modificationSwitchPoints; //cache - /** Empty link logic class - allow all linking, no guards */ private static final class Empty extends LinkLogic { @Override @@ -167,92 +162,6 @@ } /** - * Return the modification SwitchPoint of a particular index from this OptimisticBuiltins - * If none exists, one is created and that one is return. - * - * The implementor must map indexes to specific SwitchPoints for specific events and keep - * track of what they mean, for example NativeArray.LENGTH_NOT_WRITABLE_SWITCHPOINT - * might be a useful index mapping - * - * @param index index for SwitchPoint to get or create - * @return modification SwitchPoint of particular index for the receiver - */ - public SwitchPoint getOrCreateModificationSwitchPoint(final int index) { - return null; - } - - /** - * Return the modification SwitchPoint from this OptimisticBuiltins. If none - * exists, one is created and that one is return. - * - * @return modification SwitchPoint for the receiver - */ - public SwitchPoint[] getOrCreateModificationSwitchPoints() { - return null; - } - - /** - * Hook to invalidate a modification SwitchPoint by index. - * - * @param index index for SwitchPoint to invalidate - */ - public void invalidateModificationSwitchPoint(final int index) { - //empty - } - - /** - * Hook to invalidate all modification SwitchPoints for a receiver - */ - public void invalidateModificationSwitchPoints() { - //empty - } - - /** - * Check whether the receiver has an invalidated modification SwitchPoint. - * - * @param index index for the modification SwitchPoint - * @return true if the particular SwitchPoint at the index is invalidated - */ - public boolean hasInvalidatedModificationSwitchPoint(final int index) { - return false; - } - - /** - * Check whether at least one of the modification SwitchPoints has been - * invalidated - * @return true if one of the SwitchPoints has been invalidated - */ - public boolean hasInvalidatedModificationSwitchPoints() { - return false; - } - - /** - * Check whether this OptimisticBuiltins has a SwitchPoints of particular - * index. - * - * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of, - * e.g. in the constructor of every subclass. - * - * @param index index for the modification SwitchPoint - * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not - */ - public boolean hasModificationSwitchPoint(final int index) { - return false; - } - - /** - * Check whether this OptimisticBuiltins has SwitchPoints. - * - * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of, - * e.g. in the constructor of every subclass. - * - * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not - */ - public boolean hasModificationSwitchPoints() { - return false; - } - - /** * Check, given a link request and a receiver, if this specialization * fits This is used by the linker in {@link ScriptFunction} to figure * out if an optimistic builtin can be linked when first discovered @@ -265,47 +174,9 @@ * pick a non specialized target */ public boolean checkLinkable(final Object self, final CallSiteDescriptor desc, final LinkRequest request) { - // no matter what the modification switchpoints are, if any of them are invalidated, - // we can't link. Side effect is that if it's the first time we see this callsite, - // we have to create the SwitchPoint(s) so future modification switchpoint invalidations - // relink it - final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(self); - if (sps == INVALIDATED_SWITCHPOINTS) { - // nope, can't do the fast link as this assumption - // has been invalidated already, e.g. length of an - // array set to not writable - return false; - } - modificationSwitchPoints = sps; //cache - // check the link guard, if it says we can link, go ahead return canLink(self, desc, request); } - - private SwitchPoint[] getOrCreateModificationSwitchPoints(final Object self) { - final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(); //ask for all my switchpoints - if (sps != null) { //switchpoint exists, but some may be invalidated - for (final SwitchPoint sp : sps) { - if (sp.hasBeenInvalidated()) { - return INVALIDATED_SWITCHPOINTS; - } - } - } - return sps; - } - - /** - * Get the cached modification switchpoints. Only possible to do after a link - * check call has been performed, one that has answered "true", or you will get the - * wrong information. - * - * Should be used only from {@link ScriptFunction#findCallMethod} - * - * @return cached modification switchpoints for this callsite, null if none - */ - public SwitchPoint[] getModificationSwitchPoints() { - return modificationSwitchPoints == null ? null : modificationSwitchPoints.clone(); - } } /** diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/ScriptFunction.java --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Wed Nov 12 14:12:01 2014 +0100 @@ -603,16 +603,6 @@ log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc); } - final SwitchPoint[] msps = linkLogic.getModificationSwitchPoints(); - if (msps != null) { - for (final SwitchPoint sp : msps) { - if (sp != null) { - assert !sp.hasBeenInvalidated(); - sps.add(sp); - } - } - } - exceptionGuard = linkLogic.getRelinkException(); break; diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/ScriptObject.java --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java Wed Nov 12 14:12:01 2014 +0100 @@ -1352,12 +1352,9 @@ final PropertyMap selfMap = this.getMap(); final ArrayData array = getArray(); - final long length = array.length(); - - for (long i = 0; i < length; i = array.nextIndex(i)) { - if (array.has((int)i)) { - keys.add(JSType.toString(i)); - } + + for (final Iterator iter = array.indexIterator(); iter.hasNext(); ) { + keys.add(JSType.toString(iter.next().longValue())); } for (final Property property : selfMap.getProperties()) { @@ -1516,12 +1513,12 @@ * * @return {@code true} if 'length' property is non-writable */ - public final boolean isLengthNotWritable() { + public boolean isLengthNotWritable() { return (flags & IS_LENGTH_NOT_WRITABLE) != 0; } /** - * Flag this object as having non-writable length property + * Flag this object as having non-writable length property. */ public void setIsLengthNotWritable() { flags |= IS_LENGTH_NOT_WRITABLE; @@ -3151,7 +3148,6 @@ */ public final void setObject(final FindProperty find, final int callSiteFlags, final String key, final Object value) { FindProperty f = find; - if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) { final boolean isScope = NashornCallSiteDescriptor.isScopeFlag(callSiteFlags); // If the start object of the find is not this object it means the property was found inside a @@ -3177,7 +3173,6 @@ if (NashornCallSiteDescriptor.isStrictFlag(callSiteFlags)) { throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this)); } - return; } @@ -3588,7 +3583,6 @@ } return false; } - return deleteObject(JSType.toObject(key), strict); } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/ArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -30,6 +30,9 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -56,6 +59,21 @@ public static final ArrayData EMPTY_ARRAY = new UntouchedArrayData(); /** + * Length of the array data. Not necessarily length of the wrapped array. + * This is private to ensure that no one in a subclass is able to touch the length + * without going through {@link setLength}. This is used to implement + * {@link LengthNotWritableFilter}s, ensuring that there are no ways past + * a {@link setLength} function replaced by a nop + */ + private long length; + + /** + * Method handle to throw an {@link UnwarrantedOptimismException} when getting an element + * of the wrong type + */ + protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class); + + /** * Immutable empty array to get ScriptObjects started. * Use the same array and convert it to mutable as soon as it is modified */ @@ -82,7 +100,7 @@ @Override public ContinuousArrayData copy() { - return new UntouchedArrayData((int)length); + return new UntouchedArrayData((int)length()); } @Override @@ -113,6 +131,16 @@ } @Override + public ArrayData delete(final int index) { + return new DeletedRangeArrayFilter(this, index, index); + } + + @Override + public ArrayData delete(final long fromIndex, final long toIndex) { + return new DeletedRangeArrayFilter(this, fromIndex, toIndex); + } + + @Override public void shiftLeft(final int by) { //nop, always empty or we wouldn't be of this class } @@ -173,16 +201,6 @@ } @Override - public ArrayData delete(final int index) { - return new DeletedRangeArrayFilter(this, index, index); - } - - @Override - public ArrayData delete(final long fromIndex, final long toIndex) { - return new DeletedRangeArrayFilter(this, fromIndex, toIndex); - } - - @Override public Object pop() { return ScriptRuntime.UNDEFINED; } @@ -231,17 +249,6 @@ }; /** - * Length of the array data. Not necessarily length of the wrapped array. - */ - protected long length; - - /** - * Method handle to throw an {@link UnwarrantedOptimismException} when getting an element - * of the wrong type - */ - protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class); - - /** * Constructor * @param length Virtual length of the array. */ @@ -394,6 +401,16 @@ } /** + * Prevent this array from having its length reset + * + * @param underlying the underlying ArrayDAta to wrap in the non extensible filter + * @return new array data, filtered + */ + public static final ArrayData setIsLengthNotWritable(final ArrayData underlying) { + return new LengthNotWritableFilter(underlying); + } + + /** * Return the length of the array data. This may differ from the actual * length of the array this wraps as length may be set or gotten as any * other JavaScript Property @@ -446,6 +463,22 @@ } /** + * Increase length by 1 + * @return the new length, not the old one (i.e. pre-increment) + */ + protected final long increaseLength() { + return ++this.length; + } + + /** + * Decrease length by 1. + * @return the new length, not the old one (i.e. pre-decrement) + */ + protected final long decreaseLength() { + return --this.length; + } + + /** * Shift the array data left * * TODO: explore start at an index and not at zero, to make these operations @@ -454,7 +487,7 @@ * * @param by offset to shift */ - public abstract void shiftLeft(int by); + public abstract void shiftLeft(final int by); /** * Shift the array right @@ -463,7 +496,7 @@ * @return New arraydata (or same) */ - public abstract ArrayData shiftRight(int by); + public abstract ArrayData shiftRight(final int by); /** * Ensure that the given index exists and won't fail subsequent @@ -471,7 +504,7 @@ * @param safeIndex the index to ensure wont go out of bounds * @return new array data (or same) */ - public abstract ArrayData ensure(long safeIndex); + public abstract ArrayData ensure(final long safeIndex); /** * Shrink the array to a new length, may or may not retain the @@ -481,7 +514,7 @@ * * @return new array data (or same) */ - public abstract ArrayData shrink(long newLength); + public abstract ArrayData shrink(final long newLength); /** * Set an object value at a given index @@ -491,7 +524,7 @@ * @param strict are we in strict mode * @return new array data (or same) */ - public abstract ArrayData set(int index, Object value, boolean strict); + public abstract ArrayData set(final int index, final Object value, final boolean strict); /** * Set an int value at a given index @@ -501,7 +534,7 @@ * @param strict are we in strict mode * @return new array data (or same) */ - public abstract ArrayData set(int index, int value, boolean strict); + public abstract ArrayData set(final int index, final int value, final boolean strict); /** * Set a long value at a given index @@ -511,7 +544,7 @@ * @param strict are we in strict mode * @return new array data (or same) */ - public abstract ArrayData set(int index, long value, boolean strict); + public abstract ArrayData set(final int index, final long value, final boolean strict); /** * Set an double value at a given index @@ -521,7 +554,7 @@ * @param strict are we in strict mode * @return new array data (or same) */ - public abstract ArrayData set(int index, double value, boolean strict); + public abstract ArrayData set(final int index, final double value, final boolean strict); /** * Set an empty value at a given index. Should only affect Object array. @@ -552,7 +585,7 @@ * @param index the index * @return the value */ - public abstract int getInt(int index); + public abstract int getInt(final int index); /** * Returns the optimistic type of this array data. Basically, when an array data object needs to throw an @@ -581,7 +614,7 @@ * @param index the index * @return the value */ - public abstract long getLong(int index); + public abstract long getLong(final int index); /** * Get optimistic long - default is that it's impossible. Overridden @@ -601,7 +634,7 @@ * @param index the index * @return the value */ - public abstract double getDouble(int index); + public abstract double getDouble(final int index); /** * Get optimistic double - default is that it's impossible. Overridden @@ -621,14 +654,14 @@ * @param index the index * @return the value */ - public abstract Object getObject(int index); + public abstract Object getObject(final int index); /** * Tests to see if an entry exists (avoids boxing.) * @param index the index * @return true if entry exists */ - public abstract boolean has(int index); + public abstract boolean has(final int index); /** * Returns if element at specific index can be deleted or not. @@ -674,7 +707,7 @@ * @param index the index * @return new array data (or same) */ - public abstract ArrayData delete(int index); + public abstract ArrayData delete(final int index); /** * Delete a given range from this array; @@ -684,7 +717,7 @@ * * @return new ArrayData after deletion */ - public abstract ArrayData delete(long fromIndex, long toIndex); + public abstract ArrayData delete(final long fromIndex, final long toIndex); /** * Convert the ArrayData to one with a different element type @@ -694,7 +727,7 @@ * @param type new element type * @return new array data */ - public abstract ArrayData convert(Class type); + public abstract ArrayData convert(final Class type); /** * Push an array of items to the end of the array @@ -778,7 +811,7 @@ * @param to end index + 1 * @return new array data */ - public abstract ArrayData slice(long from, long to); + public abstract ArrayData slice(final long from, final long to); /** * Fast splice operation. This just modifies the array according to the number of @@ -823,6 +856,34 @@ } /** + * Return a list of keys in the array for the iterators + * @return iterator key list + */ + protected List computeIteratorKeys() { + final List keys = new ArrayList<>(); + + final long len = length(); + for (long i = 0L; i < len; i = nextIndex(i)) { + if (has((int)i)) { + keys.add(i); + } + } + + return keys; + } + + /** + * Return an iterator that goes through all indexes of elements + * in this array. This includes those after array.length if + * they exist + * + * @return iterator + */ + public Iterator indexIterator() { + return computeIteratorKeys().iterator(); + } + + /** * Exponential growth function for array size when in * need of resizing. * @@ -841,7 +902,7 @@ * * @return the next index */ - public long nextIndex(final long index) { + long nextIndex(final long index) { return index + 1; } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java --- a/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java Wed Nov 12 14:12:01 2014 +0100 @@ -39,7 +39,7 @@ protected ArrayData underlying; ArrayFilter(final ArrayData underlying) { - super(underlying.length); + super(underlying.length()); this.underlying = underlying; } @@ -70,62 +70,55 @@ @Override public void shiftLeft(final int by) { underlying.shiftLeft(by); - setLength(underlying.length); + setLength(underlying.length()); } @Override public ArrayData shiftRight(final int by) { underlying = underlying.shiftRight(by); - setLength(underlying.length); - + setLength(underlying.length()); return this; } @Override public ArrayData ensure(final long safeIndex) { underlying = underlying.ensure(safeIndex); - setLength(underlying.length); - + setLength(underlying.length()); return this; } @Override public ArrayData shrink(final long newLength) { underlying = underlying.shrink(newLength); - setLength(underlying.length); - + setLength(underlying.length()); return this; } @Override public ArrayData set(final int index, final Object value, final boolean strict) { underlying = underlying.set(index, value, strict); - setLength(underlying.length); - + setLength(underlying.length()); return this; } @Override public ArrayData set(final int index, final int value, final boolean strict) { underlying = underlying.set(index, value, strict); - setLength(underlying.length); - + setLength(underlying.length()); return this; } @Override public ArrayData set(final int index, final long value, final boolean strict) { underlying = underlying.set(index, value, strict); - setLength(underlying.length); - + setLength(underlying.length()); return this; } @Override public ArrayData set(final int index, final double value, final boolean strict) { underlying = underlying.set(index, value, strict); - setLength(underlying.length); - + setLength(underlying.length()); return this; } @@ -189,29 +182,28 @@ @Override public ArrayData delete(final int index) { underlying = underlying.delete(index); - setLength(underlying.length); + setLength(underlying.length()); return this; } @Override public ArrayData delete(final long from, final long to) { underlying = underlying.delete(from, to); - setLength(underlying.length); + setLength(underlying.length()); return this; } @Override public ArrayData convert(final Class type) { underlying = underlying.convert(type); - setLength(underlying.length); + setLength(underlying.length()); return this; } @Override public Object pop() { final Object value = underlying.pop(); - setLength(underlying.length); - + setLength(underlying.length()); return value; } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -65,7 +65,7 @@ * @return true if we don't need to do any array reallocation to fit an element at index */ public final boolean hasRoomFor(final int index) { - return has(index) || (index == length && ensure(index) == this); + return has(index) || (index == length() && ensure(index) == this); } /** @@ -73,7 +73,7 @@ * @return true if empty */ public boolean isEmpty() { - return length == 0L; + return length() == 0L; } /** diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java --- a/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java Wed Nov 12 14:12:01 2014 +0100 @@ -38,8 +38,7 @@ DeletedArrayFilter(final ArrayData underlying) { super(underlying); - - this.deleted = new BitVector(underlying.length); + this.deleted = new BitVector(underlying.length()); } @Override @@ -79,25 +78,24 @@ @Override public void shiftLeft(final int by) { super.shiftLeft(by); - deleted.shiftLeft(by, length); + deleted.shiftLeft(by, length()); } @Override public ArrayData shiftRight(final int by) { super.shiftRight(by); - deleted.shiftRight(by, length); - + deleted.shiftRight(by, length()); return this; } @Override public ArrayData ensure(final long safeIndex) { - if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) { + if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) { return new SparseArrayData(this, safeIndex + 1); } super.ensure(safeIndex); - deleted.resize(length); + deleted.resize(length()); return this; } @@ -105,36 +103,31 @@ @Override public ArrayData shrink(final long newLength) { super.shrink(newLength); - deleted.resize(length); - + deleted.resize(length()); return this; } @Override public ArrayData set(final int index, final Object value, final boolean strict) { deleted.clear(ArrayIndex.toLongIndex(index)); - return super.set(index, value, strict); } @Override public ArrayData set(final int index, final int value, final boolean strict) { deleted.clear(ArrayIndex.toLongIndex(index)); - return super.set(index, value, strict); } @Override public ArrayData set(final int index, final long value, final boolean strict) { deleted.clear(ArrayIndex.toLongIndex(index)); - return super.set(index, value, strict); } @Override public ArrayData set(final int index, final double value, final boolean strict) { deleted.clear(ArrayIndex.toLongIndex(index)); - return super.set(index, value, strict); } @@ -146,7 +139,7 @@ @Override public ArrayData delete(final int index) { final long longIndex = ArrayIndex.toLongIndex(index); - assert longIndex >= 0 && longIndex < length; + assert longIndex >= 0 && longIndex < length(); deleted.set(longIndex); underlying.setEmpty(index); return this; @@ -154,7 +147,7 @@ @Override public ArrayData delete(final long fromIndex, final long toIndex) { - assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length; + assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length(); deleted.setRange(fromIndex, toIndex + 1); underlying.setEmpty(fromIndex, toIndex); return this; @@ -162,7 +155,7 @@ @Override public Object pop() { - final long index = length - 1; + final long index = length() - 1; if (super.has((int)index)) { final boolean isDeleted = deleted.isSet(index); @@ -179,7 +172,7 @@ final ArrayData newArray = underlying.slice(from, to); final DeletedArrayFilter newFilter = new DeletedArrayFilter(newArray); newFilter.getDeleted().copy(deleted); - newFilter.getDeleted().shiftLeft(from, newFilter.length); + newFilter.getDeleted().shiftLeft(from, newFilter.length()); return newFilter; } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java --- a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java Wed Nov 12 14:12:01 2014 +0100 @@ -42,10 +42,10 @@ } private static ArrayData maybeSparse(final ArrayData underlying, final long hi) { - if(hi < SparseArrayData.MAX_DENSE_LENGTH || underlying instanceof SparseArrayData) { + if (hi < SparseArrayData.MAX_DENSE_LENGTH || underlying instanceof SparseArrayData) { return underlying; } - return new SparseArrayData(underlying, underlying.length); + return new SparseArrayData(underlying, underlying.length()); } private boolean isEmpty() { @@ -93,7 +93,7 @@ @Override public ArrayData ensure(final long safeIndex) { - if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) { + if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) { return new SparseArrayData(this, safeIndex + 1); } @@ -110,7 +110,7 @@ @Override public ArrayData shiftRight(final int by) { super.shiftRight(by); - final long len = length; + final long len = length(); lo = Math.min(len, lo + by); hi = Math.min(len - 1, hi + by); @@ -238,7 +238,7 @@ @Override public Object pop() { - final int index = (int)length - 1; + final int index = (int)length() - 1; if (super.has(index)) { final boolean isDeleted = isDeleted(index); final Object value = super.pop(); diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java --- a/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/FrozenArrayFilter.java Wed Nov 12 14:12:01 2014 +0100 @@ -26,9 +26,9 @@ package jdk.nashorn.internal.runtime.arrays; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; - import jdk.nashorn.internal.objects.Global; import jdk.nashorn.internal.runtime.PropertyDescriptor; +import jdk.nashorn.internal.runtime.ScriptRuntime; /** * ArrayData after the array has been frozen by Object.freeze call. @@ -79,4 +79,15 @@ } return this; } + + @Override + public ArrayData push(final boolean strict, final Object... items) { + return this; //nop + } + + @Override + public Object pop() { + final int len = (int)underlying.length(); + return len == 0 ? ScriptRuntime.UNDEFINED : underlying.getObject(len - 1); + } } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -119,22 +119,24 @@ @Override public IntArrayData copy() { - return new IntArrayData(array.clone(), (int)length); + return new IntArrayData(array.clone(), (int)length()); } @Override public Object asArrayOfType(final Class componentType) { if (componentType == int.class) { - return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length); + final int len = (int)length(); + return array.length == len ? array.clone() : Arrays.copyOf(array, len); } return super.asArrayOfType(componentType); } private Object[] toObjectArray(final boolean trim) { - assert length <= array.length : "length exceeds internal array size"; - final Object[] oarray = new Object[trim ? (int)length : array.length]; + assert length() <= array.length : "length exceeds internal array size"; + final int len = (int)length(); + final Object[] oarray = new Object[trim ? len : array.length]; - for (int index = 0; index < length; index++) { + for (int index = 0; index < len; index++) { oarray[index] = Integer.valueOf(array[index]); } @@ -142,10 +144,11 @@ } private double[] toDoubleArray() { - assert length <= array.length : "length exceeds internal array size"; + assert length() <= array.length : "length exceeds internal array size"; + final int len = (int)length(); final double[] darray = new double[array.length]; - for (int index = 0; index < length; index++) { + for (int index = 0; index < len; index++) { darray[index] = array[index]; } @@ -153,10 +156,11 @@ } private long[] toLongArray() { - assert length <= array.length : "length exceeds internal array size"; + assert length() <= array.length : "length exceeds internal array size"; + final int len = (int)length(); final long[] larray = new long[array.length]; - for (int index = 0; index < length; index++) { + for (int index = 0; index < len; index++) { larray[index] = array[index]; } @@ -164,15 +168,15 @@ } private LongArrayData convertToLong() { - return new LongArrayData(toLongArray(), (int)length); + return new LongArrayData(toLongArray(), (int)length()); } private NumberArrayData convertToDouble() { - return new NumberArrayData(toDoubleArray(), (int)length); + return new NumberArrayData(toDoubleArray(), (int)length()); } private ObjectArrayData convertToObject() { - return new ObjectArrayData(toObjectArray(false), (int)length); + return new ObjectArrayData(toObjectArray(false), (int)length()); } @Override @@ -196,7 +200,7 @@ @Override public ArrayData shiftRight(final int by) { - final ArrayData newData = ensure(by + length - 1); + final ArrayData newData = ensure(by + length() - 1); if (newData != this) { newData.shiftRight(by); return newData; @@ -241,7 +245,7 @@ @Override public ArrayData set(final int index, final int value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @@ -250,7 +254,7 @@ public ArrayData set(final int index, final long value, final boolean strict) { if (JSType.isRepresentableAsInt(value)) { array[index] = JSType.toInt32(value); - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @@ -261,7 +265,7 @@ public ArrayData set(final int index, final double value, final boolean strict) { if (JSType.isRepresentableAsInt(value)) { array[index] = (int)(long)value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @@ -305,7 +309,7 @@ @Override public boolean has(final int index) { - return 0 <= index && index < length; + return 0 <= index && index < length(); } @Override @@ -320,11 +324,12 @@ @Override public Object pop() { - if (length == 0) { + final int len = (int)length(); + if (len == 0) { return ScriptRuntime.UNDEFINED; } - final int newLength = (int)length - 1; + final int newLength = len - 1; final int elem = array[newLength]; array[newLength] = 0; setLength(newLength); @@ -334,12 +339,12 @@ @Override public ArrayData slice(final long from, final long to) { - return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)(to - (from < 0 ? from + length : from))); + return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)(to - (from < 0 ? from + length() : from))); } @Override public final ArrayData push(final boolean strict, final int item) { - final long len = length; + final long len = length(); final ArrayData newData = ensure(len); if (newData == this) { array[(int)len] = item; @@ -350,7 +355,7 @@ @Override public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { - final long oldLength = length; + final long oldLength = length(); final long newLength = oldLength - removed + added; if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { throw new UnsupportedOperationException(); @@ -384,21 +389,21 @@ @Override public long fastPush(final int arg) { - final int len = (int)length; + final int len = (int)length(); if (len == array.length) { array = Arrays.copyOf(array, nextSize(len)); } array[len] = arg; - return ++length; + return increaseLength(); } //length must not be zero @Override public int fastPopInt() { - if (length == 0) { + if (length() == 0) { throw new ClassCastException(); //relink } - final int newLength = (int)--length; + final int newLength = (int)decreaseLength(); final int elem = array[newLength]; array[newLength] = 0; return elem; @@ -421,8 +426,8 @@ @Override public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { - final int otherLength = (int)otherData.length; - final int thisLength = (int)length; + final int otherLength = (int)otherData.length(); + final int thisLength = (int)length(); assert otherLength > 0 && thisLength > 0; final int[] otherArray = ((IntArrayData)otherData).array; @@ -437,7 +442,7 @@ @Override public String toString() { - assert length <= array.length : length + " > " + array.length; - return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length)); + assert length() <= array.length : length() + " > " + array.length; + return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length())); } } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/LengthNotWritableFilter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/arrays/LengthNotWritableFilter.java Wed Nov 12 14:12:01 2014 +0100 @@ -0,0 +1,198 @@ +package jdk.nashorn.internal.runtime.arrays; + +import java.util.Iterator; +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; +import jdk.nashorn.internal.runtime.JSType; +import jdk.nashorn.internal.runtime.ScriptRuntime; + +/** + * Filter to use for ArrayData where the length is not writable. + * The default behavior is just to ignore {@link ArrayData#setLength} + */ +final class LengthNotWritableFilter extends ArrayFilter { + private final SortedMap extraElements; //elements with index >= length + + /** + * Constructor + * @param underlying array + */ + LengthNotWritableFilter(final ArrayData underlying) { + this(underlying, new TreeMap()); + } + + private LengthNotWritableFilter(final ArrayData underlying, final SortedMap extraElements) { + super(underlying); + this.extraElements = extraElements; + } + + @Override + public ArrayData copy() { + return new LengthNotWritableFilter(underlying.copy(), new TreeMap<>(extraElements)); + } + + @Override + public boolean has(final int index) { + return super.has(index) || extraElements.containsKey((long)index); + } + + /** + * Set the length of the data array + * + * @param length the new length for the data array + */ + @Override + public void setLength(final long length) { + //empty - setting length for a LengthNotWritableFilter is always a nop + } + + @Override + public ArrayData ensure(final long index) { + return this; + } + + @Override + public ArrayData slice(final long from, final long to) { + //return array[from...to), or array[from...length] if undefined, in this case not as we are an ArrayData + return new LengthNotWritableFilter(underlying.slice(from, to), extraElements.subMap(from, to)); + } + + private boolean checkAdd(final long index, final Object value) { + if (index >= length()) { + extraElements.put(index, value); + return true; + } + return false; + } + + private Object get(final long index) { + final Object obj = extraElements.get(index); + if (obj == null) { + return ScriptRuntime.UNDEFINED; + } + return obj; + } + + @Override + public int getInt(final int index) { + if (index >= length()) { + return JSType.toInt32(get(index)); + } + return underlying.getInt(index); + } + + @Override + public int getIntOptimistic(final int index, final int programPoint) { + if (index >= length()) { + return JSType.toInt32Optimistic(get(index), programPoint); + } + return underlying.getIntOptimistic(index, programPoint); + } + + @Override + public long getLong(final int index) { + if (index >= length()) { + return JSType.toLong(get(index)); + } + return underlying.getLong(index); + } + + @Override + public long getLongOptimistic(final int index, final int programPoint) { + if (index >= length()) { + return JSType.toLongOptimistic(get(index), programPoint); + } + return underlying.getLongOptimistic(index, programPoint); + } + + @Override + public double getDouble(final int index) { + if (index >= length()) { + return JSType.toNumber(get(index)); + } + return underlying.getDouble(index); + } + + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + if (index >= length()) { + return JSType.toNumberOptimistic(get(index), programPoint); + } + return underlying.getDoubleOptimistic(index, programPoint); + } + + @Override + public Object getObject(final int index) { + if (index >= length()) { + return get(index); + } + return underlying.getObject(index); + } + + @Override + public ArrayData set(final int index, final Object value, final boolean strict) { + if (checkAdd(index, value)) { + return this; + } + underlying = underlying.set(index, value, strict); + return this; + } + + @Override + public ArrayData set(final int index, final int value, final boolean strict) { + if (checkAdd(index, value)) { + return this; + } + underlying = underlying.set(index, value, strict); + return this; + } + + @Override + public ArrayData set(final int index, final long value, final boolean strict) { + if (checkAdd(index, value)) { + return this; + } + underlying = underlying.set(index, value, strict); + return this; + } + + @Override + public ArrayData set(final int index, final double value, final boolean strict) { + if (checkAdd(index, value)) { + return this; + } + underlying = underlying.set(index, value, strict); + return this; + } + + @Override + public ArrayData delete(final int index) { + extraElements.remove(index); + underlying = underlying.delete(index); + return this; + } + + @Override + public ArrayData delete(final long fromIndex, final long toIndex) { + for (final Iterator iter = extraElements.keySet().iterator(); iter.hasNext();) { + final long next = iter.next(); + if (next >= fromIndex && next <= toIndex) { + iter.remove(); + } + if (next > toIndex) { //ordering guaranteed because TreeSet + break; + } + } + underlying = underlying.delete(fromIndex, toIndex); + return this; + } + + @Override + public Iterator indexIterator() { + final List keys = computeIteratorKeys(); + keys.addAll(extraElements.keySet()); //even if they are outside length this is fine + return keys.iterator(); + } + +} diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -27,7 +27,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall; import static jdk.nashorn.internal.lookup.Lookup.MH; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.Arrays; @@ -77,7 +76,7 @@ @Override public LongArrayData copy() { - return new LongArrayData(array.clone(), (int)length); + return new LongArrayData(array.clone(), (int)length()); } @Override @@ -86,10 +85,11 @@ } private Object[] toObjectArray(final boolean trim) { - assert length <= array.length : "length exceeds internal array size"; - final Object[] oarray = new Object[trim ? (int)length : array.length]; + assert length() <= array.length : "length exceeds internal array size"; + final int len = (int)length(); + final Object[] oarray = new Object[trim ? len : array.length]; - for (int index = 0; index < length; index++) { + for (int index = 0; index < len; index++) { oarray[index] = Long.valueOf(array[index]); } @@ -99,16 +99,18 @@ @Override public Object asArrayOfType(final Class componentType) { if (componentType == long.class) { - return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length); + final int len = (int)length(); + return array.length == len ? array.clone() : Arrays.copyOf(array, len); } return super.asArrayOfType(componentType); } private double[] toDoubleArray() { - assert length <= array.length : "length exceeds internal array size"; + assert length() <= array.length : "length exceeds internal array size"; + final int len = (int)length(); final double[] darray = new double[array.length]; - for (int index = 0; index < length; index++) { + for (int index = 0; index < len; index++) { darray[index] = array[index]; } @@ -120,7 +122,7 @@ if (type == Integer.class || type == Long.class) { return this; } - final int len = (int)length; + final int len = (int)length(); if (type == Double.class) { return new NumberArrayData(toDoubleArray(), len); } @@ -134,7 +136,7 @@ @Override public ArrayData shiftRight(final int by) { - final ArrayData newData = ensure(by + length - 1); + final ArrayData newData = ensure(by + length() - 1); if (newData != this) { newData.shiftRight(by); return newData; @@ -179,14 +181,14 @@ @Override public ArrayData set(final int index, final int value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @Override public ArrayData set(final int index, final long value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @@ -194,7 +196,7 @@ public ArrayData set(final int index, final double value, final boolean strict) { if (JSType.isRepresentableAsLong(value)) { array[index] = (long)value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } return convert(Double.class).set(index, value, strict); @@ -265,7 +267,7 @@ @Override public boolean has(final int index) { - return 0 <= index && index < length; + return 0 <= index && index < length(); } @Override @@ -280,11 +282,12 @@ @Override public Object pop() { - if (length == 0) { + final int len = (int)length(); + if (len == 0) { return ScriptRuntime.UNDEFINED; } - final int newLength = (int)length - 1; + final int newLength = len - 1; final long elem = array[newLength]; array[newLength] = 0; setLength(newLength); @@ -294,14 +297,14 @@ @Override public ArrayData slice(final long from, final long to) { - final long start = from < 0 ? from + length : from; + final long start = from < 0 ? from + length() : from; final long newLength = to - start; return new LongArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); } @Override public final ArrayData push(final boolean strict, final long item) { - final long len = length; + final long len = length(); final ArrayData newData = ensure(len); if (newData == this) { array[(int)len] = item; @@ -312,7 +315,7 @@ @Override public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { - final long oldLength = length; + final long oldLength = length(); final long newLength = oldLength - removed + added; if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { throw new UnsupportedOperationException(); @@ -345,20 +348,20 @@ @Override public long fastPush(final long arg) { - final int len = (int)length; + final int len = (int)length(); if (len == array.length) { array = Arrays.copyOf(array, nextSize(len)); } array[len] = arg; - return ++length; + return increaseLength(); } @Override public long fastPopLong() { - if (length == 0) { - throw new ClassCastException(); + if (length() == 0) { + throw new ClassCastException(); //undefined result } - final int newLength = (int)--length; + final int newLength = (int)decreaseLength(); final long elem = array[newLength]; array[newLength] = 0; return elem; @@ -376,8 +379,8 @@ @Override public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { - final int otherLength = (int)otherData.length; - final int thisLength = (int)length; + final int otherLength = (int)otherData.length(); + final int thisLength = (int)length(); assert otherLength > 0 && thisLength > 0; final long[] otherArray = ((LongArrayData)otherData).array; @@ -392,13 +395,14 @@ @Override public String toString() { - assert length <= array.length : length + " > " + array.length; + assert length() <= array.length : length() + " > " + array.length; final StringBuilder sb = new StringBuilder(getClass().getSimpleName()). append(": ["); - for (int i = 0; i < length; i++) { + final int len = (int)length(); + for (int i = 0; i < len; i++) { sb.append(array[i]).append('L'); //make sure L suffix is on elements, to discriminate this from IntArrayData.toString() - if (i + 1 < length) { + if (i + 1 < len) { sb.append(", "); } } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java --- a/src/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/NonExtensibleArrayFilter.java Wed Nov 12 14:12:01 2014 +0100 @@ -7,13 +7,13 @@ /** * Filter class that wrap arrays that have been tagged non extensible */ -public class NonExtensibleArrayFilter extends ArrayFilter { +final class NonExtensibleArrayFilter extends ArrayFilter { /** * Constructor * @param underlying array */ - public NonExtensibleArrayFilter(final ArrayData underlying) { + NonExtensibleArrayFilter(final ArrayData underlying) { super(underlying); } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -28,7 +28,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall; import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.Arrays; @@ -76,7 +75,7 @@ @Override public NumberArrayData copy() { - return new NumberArrayData(array.clone(), (int)length); + return new NumberArrayData(array.clone(), (int)length()); } @Override @@ -85,10 +84,11 @@ } private Object[] toObjectArray(final boolean trim) { - assert length <= array.length : "length exceeds internal array size"; - final Object[] oarray = new Object[trim ? (int)length : array.length]; + assert length() <= array.length : "length exceeds internal array size"; + final int len = (int)length(); + final Object[] oarray = new Object[trim ? len : array.length]; - for (int index = 0; index < length; index++) { + for (int index = 0; index < len; index++) { oarray[index] = Double.valueOf(array[index]); } return oarray; @@ -96,8 +96,9 @@ @Override public Object asArrayOfType(final Class componentType) { - if(componentType == double.class) { - return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length); + if (componentType == double.class) { + final int len = (int)length(); + return array.length == len ? array.clone() : Arrays.copyOf(array, len); } return super.asArrayOfType(componentType); } @@ -105,7 +106,7 @@ @Override public ContinuousArrayData convert(final Class type) { if (type != Double.class && type != Integer.class && type != Long.class) { - final int len = (int)length; + final int len = (int)length(); return new ObjectArrayData(toObjectArray(false), len); } return this; @@ -118,7 +119,7 @@ @Override public ArrayData shiftRight(final int by) { - final ArrayData newData = ensure(by + length - 1); + final ArrayData newData = ensure(by + length() - 1); if (newData != this) { newData.shiftRight(by); return newData; @@ -163,21 +164,21 @@ @Override public ArrayData set(final int index, final int value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @Override public ArrayData set(final int index, final long value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @Override public ArrayData set(final int index, final double value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @@ -241,7 +242,7 @@ @Override public boolean has(final int index) { - return 0 <= index && index < length; + return 0 <= index && index < length(); } @Override @@ -256,11 +257,12 @@ @Override public Object pop() { - if (length == 0) { + final int len = (int)length(); + if (len == 0) { return UNDEFINED; } - final int newLength = (int)length - 1; + final int newLength = len - 1; final double elem = array[newLength]; array[newLength] = 0; setLength(newLength); @@ -269,14 +271,14 @@ @Override public ArrayData slice(final long from, final long to) { - final long start = from < 0 ? from + length : from; + final long start = from < 0 ? from + length() : from; final long newLength = to - start; return new NumberArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); } @Override public final ArrayData push(final boolean strict, final double item) { - final long len = length; + final long len = length(); final ArrayData newData = ensure(len); if (newData == this) { array[(int)len] = item; @@ -287,7 +289,7 @@ @Override public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { - final long oldLength = length; + final long oldLength = length(); final long newLength = oldLength - removed + added; if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { throw new UnsupportedOperationException(); @@ -325,21 +327,21 @@ @Override public long fastPush(final double arg) { - final int len = (int)length; + final int len = (int)length(); if (len == array.length) { //note that fastpush never creates spares arrays, there is nothing to gain by that - it will just use even more memory array = Arrays.copyOf(array, nextSize(len)); } array[len] = arg; - return ++length; + return increaseLength(); } @Override public double fastPopDouble() { - if (length == 0) { + if (length() == 0) { throw new ClassCastException(); } - final int newLength = (int)--length; + final int newLength = (int)decreaseLength(); final double elem = array[newLength]; array[newLength] = 0; return elem; @@ -352,8 +354,8 @@ @Override public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { - final int otherLength = (int)otherData.length; - final int thisLength = (int)length; + final int otherLength = (int)otherData.length(); + final int thisLength = (int)length(); assert otherLength > 0 && thisLength > 0; final double[] otherArray = ((NumberArrayData)otherData).array; @@ -368,7 +370,7 @@ @Override public String toString() { - assert length <= array.length : length + " > " + array.length; - return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length)); + assert length() <= array.length : length() + " > " + array.length; + return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length())); } } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -26,7 +26,6 @@ package jdk.nashorn.internal.runtime.arrays; import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.Arrays; @@ -77,16 +76,16 @@ @Override public ObjectArrayData copy() { - return new ObjectArrayData(array.clone(), (int)length); + return new ObjectArrayData(array.clone(), (int)length()); } @Override public Object[] asObjectArray() { - return array.length == length ? array.clone() : asObjectArrayCopy(); + return array.length == length() ? array.clone() : asObjectArrayCopy(); } private Object[] asObjectArrayCopy() { - final long len = length; + final long len = length(); assert len <= Integer.MAX_VALUE; final Object[] copy = new Object[(int)len]; System.arraycopy(array, 0, copy, 0, (int)len); @@ -105,7 +104,7 @@ @Override public ArrayData shiftRight(final int by) { - final ArrayData newData = ensure(by + length - 1); + final ArrayData newData = ensure(by + length() - 1); if (newData != this) { newData.shiftRight(by); return newData; @@ -137,28 +136,28 @@ @Override public ArrayData set(final int index, final Object value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @Override public ArrayData set(final int index, final int value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @Override public ArrayData set(final int index, final long value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @Override public ArrayData set(final int index, final double value, final boolean strict) { array[index] = value; - setLength(Math.max(index + 1, length)); + setLength(Math.max(index + 1, length())); return this; } @@ -231,7 +230,7 @@ @Override public boolean has(final int index) { - return 0 <= index && index < length; + return 0 <= index && index < length(); } @Override @@ -263,20 +262,20 @@ @Override public long fastPush(final Object arg) { - final int len = (int)length; + final int len = (int)length(); if (len == array.length) { array = Arrays.copyOf(array, nextSize(len)); } array[len] = arg; - return ++length; + return increaseLength(); } @Override public Object fastPopObject() { - if (length == 0) { + if (length() == 0) { return ScriptRuntime.UNDEFINED; } - final int newLength = (int)--length; + final int newLength = (int)decreaseLength(); final Object elem = array[newLength]; array[newLength] = ScriptRuntime.EMPTY; return elem; @@ -284,11 +283,11 @@ @Override public Object pop() { - if (length == 0) { + if (length() == 0) { return ScriptRuntime.UNDEFINED; } - final int newLength = (int)length - 1; + final int newLength = (int)length() - 1; final Object elem = array[newLength]; setEmpty(newLength); setLength(newLength); @@ -297,14 +296,14 @@ @Override public ArrayData slice(final long from, final long to) { - final long start = from < 0 ? from + length : from; + final long start = from < 0 ? from + length() : from; final long newLength = to - start; return new ObjectArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength); } @Override public ArrayData push(final boolean strict, final Object item) { - final long len = length; + final long len = length(); final ArrayData newData = ensure(len); if (newData == this) { array[(int)len] = item; @@ -315,7 +314,7 @@ @Override public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException { - final long oldLength = length; + final long oldLength = length(); final long newLength = oldLength - removed + added; if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) { throw new UnsupportedOperationException(); @@ -343,8 +342,8 @@ @Override public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) { - final int otherLength = (int)otherData.length; - final int thisLength = (int)length; + final int otherLength = (int)otherData.length(); + final int thisLength = (int)length(); assert otherLength > 0 && thisLength > 0; final Object[] otherArray = ((ObjectArrayData)otherData).array; @@ -359,7 +358,7 @@ @Override public String toString() { - assert length <= array.length : length + " > " + array.length; - return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length)); + assert length() <= array.length : length() + " > " + array.length; + return getClass().getSimpleName() + ':' + Arrays.toString(Arrays.copyOf(array, (int)length())); } } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -53,21 +53,21 @@ SparseArrayData(final ArrayData underlying, final long length, final TreeMap sparseMap) { super(length); - assert underlying.length <= length; + assert underlying.length() <= length; this.underlying = underlying; - this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length); + this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length()); this.sparseMap = sparseMap; } @Override public ArrayData copy() { - return new SparseArrayData(underlying.copy(), length, new TreeMap<>(sparseMap)); + return new SparseArrayData(underlying.copy(), length(), new TreeMap<>(sparseMap)); } @Override public Object[] asObjectArray() { - final int len = (int)Math.min(length, Integer.MAX_VALUE); - final int underlyingLength = (int)Math.min(len, underlying.length); + final int len = (int)Math.min(length(), Integer.MAX_VALUE); + final int underlyingLength = (int)Math.min(len, underlying.length()); final Object[] objArray = new Object[len]; for (int i = 0; i < underlyingLength; i++) { @@ -104,14 +104,15 @@ } sparseMap = newSparseMap; - setLength(Math.max(length - by, 0)); + setLength(Math.max(length() - by, 0)); } @Override public ArrayData shiftRight(final int by) { final TreeMap newSparseMap = new TreeMap<>(); - if (underlying.length + by > maxDenseLength) { - for (long i = maxDenseLength - by; i < underlying.length; i++) { + final long len = underlying.length(); + if (len + by > maxDenseLength) { + for (long i = maxDenseLength - by; i < len; i++) { if (underlying.has((int) i)) { newSparseMap.put(Long.valueOf(i + by), underlying.getObject((int) i)); } @@ -127,23 +128,23 @@ } sparseMap = newSparseMap; - setLength(length + by); + setLength(length() + by); return this; } @Override public ArrayData ensure(final long safeIndex) { - if (safeIndex < maxDenseLength && underlying.length <= safeIndex) { + if (safeIndex < maxDenseLength && underlying.length() <= safeIndex) { underlying = underlying.ensure(safeIndex); } - setLength(Math.max(safeIndex + 1, length)); + setLength(Math.max(safeIndex + 1, length())); return this; } @Override public ArrayData shrink(final long newLength) { - if (newLength < underlying.length) { + if (newLength < underlying.length()) { underlying = underlying.shrink(newLength); underlying.setLength(newLength); sparseMap.clear(); @@ -160,11 +161,11 @@ if (index >= 0 && index < maxDenseLength) { ensure(index); underlying = underlying.set(index, value, strict); - setLength(Math.max(underlying.length, length)); + setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); sparseMap.put(longIndex, value); - setLength(Math.max(longIndex + 1, length)); + setLength(Math.max(longIndex + 1, length())); } return this; @@ -175,11 +176,11 @@ if (index >= 0 && index < maxDenseLength) { ensure(index); underlying = underlying.set(index, value, strict); - setLength(Math.max(underlying.length, length)); + setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); sparseMap.put(longIndex, value); - setLength(Math.max(longIndex + 1, length)); + setLength(Math.max(longIndex + 1, length())); } return this; } @@ -189,11 +190,11 @@ if (index >= 0 && index < maxDenseLength) { ensure(index); underlying = underlying.set(index, value, strict); - setLength(Math.max(underlying.length, length)); + setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); sparseMap.put(longIndex, value); - setLength(Math.max(longIndex + 1, length)); + setLength(Math.max(longIndex + 1, length())); } return this; } @@ -203,11 +204,11 @@ if (index >= 0 && index < maxDenseLength) { ensure(index); underlying = underlying.set(index, value, strict); - setLength(Math.max(underlying.length, length)); + setLength(Math.max(underlying.length(), length())); } else { final Long longIndex = indexToKey(index); sparseMap.put(longIndex, value); - setLength(Math.max(longIndex + 1, length)); + setLength(Math.max(longIndex + 1, length())); } return this; } @@ -294,7 +295,7 @@ @Override public boolean has(final int index) { if (index >= 0 && index < maxDenseLength) { - return index < underlying.length && underlying.has(index); + return index < underlying.length() && underlying.has(index); } return sparseMap.containsKey(indexToKey(index)); @@ -303,7 +304,7 @@ @Override public ArrayData delete(final int index) { if (index >= 0 && index < maxDenseLength) { - if (index < underlying.length) { + if (index < underlying.length()) { underlying = underlying.delete(index); } } else { @@ -315,8 +316,8 @@ @Override public ArrayData delete(final long fromIndex, final long toIndex) { - if (fromIndex < maxDenseLength && fromIndex < underlying.length) { - underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length - 1)); + if (fromIndex < maxDenseLength && fromIndex < underlying.length()) { + underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length() - 1)); } if (toIndex >= maxDenseLength) { sparseMap.subMap(fromIndex, true, toIndex, true).clear(); @@ -336,30 +337,34 @@ @Override public Object pop() { - if (length == 0) { + final long len = length(); + final long underlyingLen = underlying.length(); + if (len == 0) { return ScriptRuntime.UNDEFINED; } - if (length == underlying.length) { + if (len == underlyingLen) { final Object result = underlying.pop(); - setLength(underlying.length); + setLength(underlying.length()); return result; } - setLength(length - 1); - final Long key = Long.valueOf(length); + setLength(len - 1); + final Long key = Long.valueOf(len - 1); return sparseMap.containsKey(key) ? sparseMap.remove(key) : ScriptRuntime.UNDEFINED; } @Override public ArrayData slice(final long from, final long to) { - assert to <= length; - final long start = from < 0 ? (from + length) : from; + assert to <= length(); + final long start = from < 0 ? (from + length()) : from; final long newLength = to - start; + final long underlyingLength = underlying.length(); + if (start >= 0 && to <= maxDenseLength) { - if (newLength <= underlying.length) { + if (newLength <= underlyingLength) { return underlying.slice(from, to); } - return underlying.slice(from, to).ensure(newLength - 1).delete(underlying.length, newLength); + return underlying.slice(from, to).ensure(newLength - 1).delete(underlyingLength, newLength); } ArrayData sliced = EMPTY_ARRAY; @@ -369,13 +374,13 @@ sliced = sliced.set((int)(i - start), getObject((int)i), false); } } - assert sliced.length == newLength; + assert sliced.length() == newLength; return sliced; } @Override public long nextIndex(final long index) { - if (index < underlying.length - 1) { + if (index < underlying.length() - 1) { return underlying.nextIndex(index); } @@ -383,6 +388,7 @@ if (nextKey != null) { return nextKey; } - return length; + + return length(); } } diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java --- a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java Wed Nov 12 14:12:01 2014 +0100 @@ -58,7 +58,7 @@ * @return element length */ public final int getElementLength() { - return (int)length; + return (int)length(); } /** @@ -119,7 +119,7 @@ @Override public final boolean has(final int index) { - return 0 <= index && index < length; + return 0 <= index && index < length(); } @Override diff -r 56c0d55ea562 -r 3dbb4c9ff43c src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java --- a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java Wed Nov 12 14:54:40 2014 +0100 +++ b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java Wed Nov 12 14:12:01 2014 +0100 @@ -39,8 +39,7 @@ UndefinedArrayFilter(final ArrayData underlying) { super(underlying); - - this.undefined = new BitVector(underlying.length); + this.undefined = new BitVector(underlying.length()); } @Override @@ -80,25 +79,24 @@ @Override public void shiftLeft(final int by) { super.shiftLeft(by); - undefined.shiftLeft(by, length); + undefined.shiftLeft(by, length()); } @Override public ArrayData shiftRight(final int by) { super.shiftRight(by); - undefined.shiftRight(by, length); - + undefined.shiftRight(by, length()); return this; } @Override public ArrayData ensure(final long safeIndex) { - if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) { + if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) { return new SparseArrayData(this, safeIndex + 1); } super.ensure(safeIndex); - undefined.resize(length); + undefined.resize(length()); return this; } @@ -106,8 +104,7 @@ @Override public ArrayData shrink(final long newLength) { super.shrink(newLength); - undefined.resize(length); - + undefined.resize(length()); return this; } @@ -216,7 +213,7 @@ @Override public Object pop() { - final long index = length - 1; + final long index = length() - 1; if (super.has((int)index)) { final boolean isUndefined = undefined.isSet(index); @@ -233,7 +230,7 @@ final ArrayData newArray = underlying.slice(from, to); final UndefinedArrayFilter newFilter = new UndefinedArrayFilter(newArray); newFilter.getUndefined().copy(undefined); - newFilter.getUndefined().shiftLeft(from, newFilter.length); + newFilter.getUndefined().shiftLeft(from, newFilter.length()); return newFilter; } diff -r 56c0d55ea562 -r 3dbb4c9ff43c test/script/basic/JDK-8035312.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8035312.js Wed Nov 12 14:12:01 2014 +0100 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8035312 push to frozen array must not increase length property + * + * @test + * @run + * @fork + * @option -Dnashorn.debug=true + */ + +function printArrayDataClass(x) { + if (typeof Debug !== 'undefined') { + print(Debug.getArrayDataClass(x)); + } +} + +function gpush(x, elem) { + try { + print("Pushing " + elem + " to " + x); + x.push(elem); + } catch (e) { + print("caught error" + e); + } + print("\tarray is now [" + x + "] length is = " + x.length); + print(); + printArrayDataClass(x); +} + +function gpop(x) { + try { + print("Popping from " + x); + x.pop(); + } catch (e) { + if (!(e instanceof TypeError)) { + print("e of wrong type " + e); + } + } + print("\tarray is now [" + x + "] length is = " + x.length); + print(); + printArrayDataClass(x); +} + +function checkArray(x) { + print(); + print(">>> Push test"); + + var olen = x.length; + gpush(x, 0); + + print("x.length === " + x.length + " (should be " + olen + ")"); + print("x[3] === " + x[3] + " (should be 0)"); + print("x[4] === " + x[4] + " (should be undefined)"); + + print(); + print(">>> Pop test"); + gpop(x); + gpop(x); + print("x.length === " + x.length + " (should be " + olen + ")"); + print("x === " + x); + + for (var i = 0 ; i < 5; i++) { + gpop(x); + } + + print("x.length === " + x.length + " (should be " + olen + ")"); + print("x === " + x); +} + +print("*** Freezing"); +var frozen = [1,2,3]; +Object.freeze(frozen); +checkArray(frozen); +printArrayDataClass(frozen); + +//so far so good + +print(); +print("*** Other length not writable issues"); +var lengthNotWritable = [1,2,3]; +Object.defineProperty(lengthNotWritable, "length", { writable: false }); +checkArray(lengthNotWritable); +printArrayDataClass(lengthNotWritable); + +function set(array, from, to, stride) { + //add three elements + for (var i = from; i < to; i+=stride) { + try { + print("Writing " + i); + array[i] = i; + printArrayDataClass(array); + } catch (e) { + print(e instanceof TypeError); + } + } +} + +//define empty array with non writable length +var arr = [1]; +Object.defineProperty(arr, "length", { writable: false }); + +var olen2 = arr.length; + +set(arr, 0, 3, 1); + +if (arr.length != olen2) { + throw new ("error: " + arr.length + " != " + olen2); +} + +print(); +print("array writing 0-3, with 1 stride, array = " + arr); +print("length = " + arr.length + ", but elements are: " + arr[0] + " " + arr[1] + " " + arr[2]); +print(); + +//do the same but sparse/deleted range +var arr2 = [1]; +Object.defineProperty(arr2, "length", { writable: false }); + +print("initial length = " + arr2.length); +var olen3 = arr2.length; + +set(arr2, 0, 30, 3); + +if (arr2.length != olen3) { + throw new ("error: " + arr2.length + " != " + olen3); +} + +print(); +var larger = 20; +print("array writing 0-" + larger + ", with 3 stride, array = " + arr2); +print("length = " + arr2.length + ", but elements are: " + arr2[0] + " " + arr2[1] + " " + arr2[2]); + +for (var i = 0; i < larger; i++) { + if (arr2[i] === undefined) { + continue; + } + print(arr2[i] + " has length " + arr2.length); +} + +print(); +var elem = 0x7fffffff - 10; +printArrayDataClass(arr2); +print("adding a new element high up in the array"); +print("length before element was added " + arr2.length); +print("putting sparse at " + elem); +arr2[elem] = "sparse"; +print("length after element was added " + arr2.length + " should be the same"); +printArrayDataClass(arr2); + +print(); +print("Printing arr2 - this will fail if length is > 28 and it is " + arr2.length); +print("arr2 = [" + arr2 + "]"); +print("new length that should not be writable = " + arr2.length); +print(arr2[elem] === "sparse"); +print(arr2[elem]); +for (var i = 0; i < larger; i++) { + print(arr2[i]); +} +for (var key in arr2) { + print(key + ":" + arr2[key]); +} + +//issues reported by sundar - generic setter doesn't go through push/pop bulkable + +function sundarExample2(arr, _writable) { + print("Checking if push works for bulkable non bulkable arrays - Setting length property not allowed"); + arr[0] = "bar"; + print(arr.length + " should be 1"); // should be 1 + print(arr[0] + " should be bar"); + print("["+ arr + "] should be [bar]"); + + // Object.defineProperty(arr, "length", { configurable: _writable }); + Object.defineProperty(arr, "length", { writable: _writable }); + arr[1] = "baz"; + + if (_writable) { + print(arr.length + " should be 2"); + print(arr[0] + " should be bar"); + print(arr[1] + " should be baz"); + print("["+ arr + "] should be [bar,baz]"); + } else { + print(arr.length + " should STILL be 1"); + print(arr[0] + " should be bar"); + print(arr[1] + " should be baz"); + print("["+ arr + "] should be [bar]"); + } +} + +var newArr1 = []; +sundarExample2(newArr1, false); +print(); +try { + sundarExample2(newArr1, true); + print("should not get here"); +} catch (e) { + if (!(e instanceof TypeError)) { + print("Wrong exception"); + } + print("got TypeError when redefining length, as expected") +} +print(); + +sundarExample2([], true); +print("Done"); diff -r 56c0d55ea562 -r 3dbb4c9ff43c test/script/basic/JDK-8035312.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8035312.js.EXPECTED Wed Nov 12 14:12:01 2014 +0100 @@ -0,0 +1,186 @@ +*** Freezing + +>>> Push test +Pushing 0 to 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +x.length === 3 (should be 3) +x[3] === undefined (should be 0) +x[4] === undefined (should be undefined) + +>>> Pop test +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +x.length === 3 (should be 3) +x === 1,2,3 +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter +x.length === 3 (should be 3) +x === 1,2,3 +class jdk.nashorn.internal.runtime.arrays.FrozenArrayFilter + +*** Other length not writable issues + +>>> Push test +Pushing 0 to 1,2,3 +caught errorTypeError: "length" is not a writable property of [object Array] + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +x.length === 3 (should be 3) +x[3] === 0 (should be 0) +x[4] === undefined (should be undefined) + +>>> Pop test +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +x.length === 3 (should be 3) +x === 1,2,3 +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Popping from 1,2,3 + array is now [1,2,3] length is = 3 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +x.length === 3 (should be 3) +x === 1,2,3 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 0 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 1 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 2 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter + +array writing 0-3, with 1 stride, array = 0 +length = 1, but elements are: 0 undefined 2 + +initial length = 1 +Writing 0 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 3 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 6 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 9 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 12 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 15 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 18 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 21 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 24 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +Writing 27 +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter + +array writing 0-20, with 3 stride, array = 0 +length = 1, but elements are: 0 undefined undefined +0 has length 1 + +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter +adding a new element high up in the array +length before element was added 1 +putting sparse at 2147483637 +length after element was added 1 should be the same +class jdk.nashorn.internal.runtime.arrays.LengthNotWritableFilter + +Printing arr2 - this will fail if length is > 28 and it is 1 +arr2 = [0] +new length that should not be writable = 1 +true +sparse +0 +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +undefined +0:0 +2147483637:sparse +Checking if push works for bulkable non bulkable arrays - Setting length property not allowed +1 should be 1 +bar should be bar +[bar] should be [bar] +1 should STILL be 1 +bar should be bar +baz should be baz +[bar] should be [bar] + +Checking if push works for bulkable non bulkable arrays - Setting length property not allowed +1 should be 1 +bar should be bar +[bar] should be [bar] +got TypeError when redefining length, as expected + +Checking if push works for bulkable non bulkable arrays - Setting length property not allowed +1 should be 1 +bar should be bar +[bar] should be [bar] +2 should be 2 +bar should be bar +baz should be baz +[bar,baz] should be [bar,baz] +Done diff -r 56c0d55ea562 -r 3dbb4c9ff43c test/script/basic/JDK-8035312_2.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8035312_2.js Wed Nov 12 14:12:01 2014 +0100 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8035312_2 - length setter and iterators + * + * @test + * @run + */ + +"use strict" + +function printArray(a,n) { + print("PRINT_ARRAY CALLED: length = " + a.length); + print(); + + print("INDEXED"); + for (var x = 0; x