src/jdk/nashorn/internal/objects/NativeArray.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1828
92b5c838f6d5
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

Merge

     1 /*
     2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.objects;
    28 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
    29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    30 import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
    31 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
    32 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
    33 import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
    34 import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
    35 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
    37 import java.lang.invoke.MethodHandle;
    38 import java.util.ArrayList;
    39 import java.util.Arrays;
    40 import java.util.Collections;
    41 import java.util.Comparator;
    42 import java.util.Iterator;
    43 import java.util.List;
    44 import java.util.concurrent.Callable;
    45 import jdk.internal.dynalink.CallSiteDescriptor;
    46 import jdk.internal.dynalink.linker.GuardedInvocation;
    47 import jdk.internal.dynalink.linker.LinkRequest;
    48 import jdk.nashorn.api.scripting.JSObject;
    49 import jdk.nashorn.internal.objects.annotations.Attribute;
    50 import jdk.nashorn.internal.objects.annotations.Constructor;
    51 import jdk.nashorn.internal.objects.annotations.Function;
    52 import jdk.nashorn.internal.objects.annotations.Getter;
    53 import jdk.nashorn.internal.objects.annotations.ScriptClass;
    54 import jdk.nashorn.internal.objects.annotations.Setter;
    55 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
    56 import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
    57 import jdk.nashorn.internal.objects.annotations.Where;
    58 import jdk.nashorn.internal.runtime.Context;
    59 import jdk.nashorn.internal.runtime.Debug;
    60 import jdk.nashorn.internal.runtime.JSType;
    61 import jdk.nashorn.internal.runtime.OptimisticBuiltins;
    62 import jdk.nashorn.internal.runtime.PropertyDescriptor;
    63 import jdk.nashorn.internal.runtime.PropertyMap;
    64 import jdk.nashorn.internal.runtime.ScriptFunction;
    65 import jdk.nashorn.internal.runtime.ScriptObject;
    66 import jdk.nashorn.internal.runtime.ScriptRuntime;
    67 import jdk.nashorn.internal.runtime.Undefined;
    68 import jdk.nashorn.internal.runtime.arrays.ArrayData;
    69 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
    70 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
    71 import jdk.nashorn.internal.runtime.arrays.ContinuousArrayData;
    72 import jdk.nashorn.internal.runtime.arrays.IntElements;
    73 import jdk.nashorn.internal.runtime.arrays.IteratorAction;
    74 import jdk.nashorn.internal.runtime.arrays.NumericElements;
    75 import jdk.nashorn.internal.runtime.linker.Bootstrap;
    76 import jdk.nashorn.internal.runtime.linker.InvokeByName;
    78 /**
    79  * Runtime representation of a JavaScript array. NativeArray only holds numeric
    80  * keyed values. All other values are stored in spill.
    81  */
    82 @ScriptClass("Array")
    83 public final class NativeArray extends ScriptObject implements OptimisticBuiltins {
    84     private static final Object JOIN                     = new Object();
    85     private static final Object EVERY_CALLBACK_INVOKER   = new Object();
    86     private static final Object SOME_CALLBACK_INVOKER    = new Object();
    87     private static final Object FOREACH_CALLBACK_INVOKER = new Object();
    88     private static final Object MAP_CALLBACK_INVOKER     = new Object();
    89     private static final Object FILTER_CALLBACK_INVOKER  = new Object();
    90     private static final Object REDUCE_CALLBACK_INVOKER  = new Object();
    91     private static final Object CALL_CMP                 = new Object();
    92     private static final Object TO_LOCALE_STRING         = new Object();
    94     /*
    95      * Constructors.
    96      */
    97     NativeArray() {
    98         this(ArrayData.initialArray());
    99     }
   101     NativeArray(final long length) {
   102         this(ArrayData.allocate(length));
   103     }
   105     NativeArray(final int[] array) {
   106         this(ArrayData.allocate(array));
   107     }
   109     NativeArray(final double[] array) {
   110         this(ArrayData.allocate(array));
   111     }
   113     NativeArray(final long[] array) {
   114         this(ArrayData.allocate(array.length));
   116         ArrayData arrayData = this.getArray();
   117         Class<?> widest = int.class;
   119         for (int index = 0; index < array.length; index++) {
   120             final long value = array[index];
   122             if (widest == int.class && JSType.isRepresentableAsInt(value)) {
   123                 arrayData = arrayData.set(index, (int) value, false);
   124             } else if (widest != Object.class && JSType.isRepresentableAsDouble(value)) {
   125                 arrayData = arrayData.set(index, (double) value, false);
   126                 widest = double.class;
   127             } else {
   128                 arrayData = arrayData.set(index, (Object) value, false);
   129                 widest = Object.class;
   130             }
   131         }
   133         this.setArray(arrayData);
   134     }
   136     NativeArray(final Object[] array) {
   137         this(ArrayData.allocate(array.length));
   139         ArrayData arrayData = this.getArray();
   141         for (int index = 0; index < array.length; index++) {
   142             final Object value = array[index];
   144             if (value == ScriptRuntime.EMPTY) {
   145                 arrayData = arrayData.delete(index);
   146             } else {
   147                 arrayData = arrayData.set(index, value, false);
   148             }
   149         }
   151         this.setArray(arrayData);
   152     }
   154     NativeArray(final ArrayData arrayData) {
   155         this(arrayData, Global.instance());
   156     }
   158     NativeArray(final ArrayData arrayData, final Global global) {
   159         super(global.getArrayPrototype(), $nasgenmap$);
   160         setArray(arrayData);
   161         setIsArray();
   162     }
   164     @Override
   165     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
   166         final GuardedInvocation inv = getArray().findFastGetMethod(getArray().getClass(), desc, request, operator);
   167         if (inv != null) {
   168             return inv;
   169         }
   170         return super.findGetMethod(desc, request, operator);
   171     }
   173     @Override
   174     protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
   175         final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request);
   176         if (inv != null) {
   177             return inv;
   178         }
   179         return super.findGetIndexMethod(desc, request);
   180     }
   182     @Override
   183     protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
   184         final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request);
   185         if (inv != null) {
   186             return inv;
   187         }
   189         return super.findSetIndexMethod(desc, request);
   190     }
   192     private static InvokeByName getJOIN() {
   193         return Global.instance().getInvokeByName(JOIN,
   194                 new Callable<InvokeByName>() {
   195                     @Override
   196                     public InvokeByName call() {
   197                         return new InvokeByName("join", ScriptObject.class);
   198                     }
   199                 });
   200     }
   202     private static MethodHandle createIteratorCallbackInvoker(final Object key, final Class<?> rtype) {
   203         return Global.instance().getDynamicInvoker(key,
   204             new Callable<MethodHandle>() {
   205                 @Override
   206                 public MethodHandle call() {
   207                     return Bootstrap.createDynamicInvoker("dyn:call", rtype, Object.class, Object.class, Object.class,
   208                         double.class, Object.class);
   209                 }
   210             });
   211     }
   213     private static MethodHandle getEVERY_CALLBACK_INVOKER() {
   214         return createIteratorCallbackInvoker(EVERY_CALLBACK_INVOKER, boolean.class);
   215     }
   217     private static MethodHandle getSOME_CALLBACK_INVOKER() {
   218         return createIteratorCallbackInvoker(SOME_CALLBACK_INVOKER, boolean.class);
   219     }
   221     private static MethodHandle getFOREACH_CALLBACK_INVOKER() {
   222         return createIteratorCallbackInvoker(FOREACH_CALLBACK_INVOKER, void.class);
   223     }
   225     private static MethodHandle getMAP_CALLBACK_INVOKER() {
   226         return createIteratorCallbackInvoker(MAP_CALLBACK_INVOKER, Object.class);
   227     }
   229     private static MethodHandle getFILTER_CALLBACK_INVOKER() {
   230         return createIteratorCallbackInvoker(FILTER_CALLBACK_INVOKER, boolean.class);
   231     }
   233     private static MethodHandle getREDUCE_CALLBACK_INVOKER() {
   234         return Global.instance().getDynamicInvoker(REDUCE_CALLBACK_INVOKER,
   235                 new Callable<MethodHandle>() {
   236                     @Override
   237                     public MethodHandle call() {
   238                         return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class,
   239                              Undefined.class, Object.class, Object.class, double.class, Object.class);
   240                     }
   241                 });
   242     }
   244     private static MethodHandle getCALL_CMP() {
   245         return Global.instance().getDynamicInvoker(CALL_CMP,
   246                 new Callable<MethodHandle>() {
   247                     @Override
   248                     public MethodHandle call() {
   249                         return Bootstrap.createDynamicInvoker("dyn:call", double.class,
   250                             Object.class, Object.class, Object.class, Object.class);
   251                     }
   252                 });
   253     }
   255     private static InvokeByName getTO_LOCALE_STRING() {
   256         return Global.instance().getInvokeByName(TO_LOCALE_STRING,
   257                 new Callable<InvokeByName>() {
   258                     @Override
   259                     public InvokeByName call() {
   260                         return new InvokeByName("toLocaleString", ScriptObject.class, String.class);
   261                     }
   262                 });
   263     }
   265     // initialized by nasgen
   266     private static PropertyMap $nasgenmap$;
   268     @Override
   269     public String getClassName() {
   270         return "Array";
   271     }
   273     @Override
   274     public Object getLength() {
   275         final long length = getArray().length();
   276         assert length >= 0L;
   277         if (length <= Integer.MAX_VALUE) {
   278             return (int)length;
   279         }
   280         return length;
   281     }
   283     private boolean defineLength(final long oldLen, final PropertyDescriptor oldLenDesc, final PropertyDescriptor desc, final boolean reject) {
   284         // Step 3a
   285         if (!desc.has(VALUE)) {
   286             return super.defineOwnProperty("length", desc, reject);
   287         }
   289         // Step 3b
   290         final PropertyDescriptor newLenDesc = desc;
   292         // Step 3c and 3d - get new length and convert to long
   293         final long newLen = NativeArray.validLength(newLenDesc.getValue());
   295         // Step 3e - note that we need to convert to int or double as long is not considered a JS number type anymore
   296         newLenDesc.setValue(JSType.toNarrowestNumber(newLen));
   298         // Step 3f
   299         // increasing array length - just need to set new length value (and attributes if any) and return
   300         if (newLen >= oldLen) {
   301             return super.defineOwnProperty("length", newLenDesc, reject);
   302         }
   304         // Step 3g
   305         if (!oldLenDesc.isWritable()) {
   306             if (reject) {
   307                 throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
   308             }
   309             return false;
   310         }
   312         // Step 3h and 3i
   313         final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable();
   314         if (!newWritable) {
   315             newLenDesc.setWritable(true);
   316         }
   318         // Step 3j and 3k
   319         final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject);
   320         if (!succeeded) {
   321             return false;
   322         }
   324         // Step 3l
   325         // make sure that length is set till the point we can delete the old elements
   326         long o = oldLen;
   327         while (newLen < o) {
   328             o--;
   329             final boolean deleteSucceeded = delete(o, false);
   330             if (!deleteSucceeded) {
   331                 newLenDesc.setValue(o + 1);
   332                 if (!newWritable) {
   333                     newLenDesc.setWritable(false);
   334                 }
   335                 super.defineOwnProperty("length", newLenDesc, false);
   336                 if (reject) {
   337                     throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
   338                 }
   339                 return false;
   340             }
   341         }
   343         // Step 3m
   344         if (!newWritable) {
   345             // make 'length' property not writable
   346             final ScriptObject newDesc = Global.newEmptyInstance();
   347             newDesc.set(WRITABLE, false, 0);
   348             return super.defineOwnProperty("length", newDesc, false);
   349         }
   351         return true;
   352     }
   354     /**
   355      * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
   356      */
   357     @Override
   358     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
   359         final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc);
   361         // never be undefined as "length" is always defined and can't be deleted for arrays
   362         // Step 1
   363         final PropertyDescriptor oldLenDesc = (PropertyDescriptor) super.getOwnPropertyDescriptor("length");
   365         // Step 2
   366         // get old length and convert to long. Always a Long/Uint32 but we take the safe road.
   367         final long oldLen = JSType.toUint32(oldLenDesc.getValue());
   369         // Step 3
   370         if ("length".equals(key)) {
   371             // check for length being made non-writable
   372             final boolean result = defineLength(oldLen, oldLenDesc, desc, reject);
   373             if (desc.has(WRITABLE) && !desc.isWritable()) {
   374                 setIsLengthNotWritable();
   375             }
   376             return result;
   377         }
   379         // Step 4a
   380         final int index = ArrayIndex.getArrayIndex(key);
   381         if (ArrayIndex.isValidArrayIndex(index)) {
   382             final long longIndex = ArrayIndex.toLongIndex(index);
   383             // Step 4b
   384             // setting an element beyond current length, but 'length' is not writable
   385             if (longIndex >= oldLen && !oldLenDesc.isWritable()) {
   386                 if (reject) {
   387                     throw typeError("property.not.writable", Long.toString(longIndex), ScriptRuntime.safeToString(this));
   388                 }
   389                 return false;
   390             }
   392             // Step 4c
   393             // set the new array element
   394             final boolean succeeded = super.defineOwnProperty(key, desc, false);
   396             // Step 4d
   397             if (!succeeded) {
   398                 if (reject) {
   399                     throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
   400                 }
   401                 return false;
   402             }
   404             // Step 4e -- adjust new length based on new element index that is set
   405             if (longIndex >= oldLen) {
   406                 oldLenDesc.setValue(longIndex + 1);
   407                 super.defineOwnProperty("length", oldLenDesc, false);
   408             }
   410             // Step 4f
   411             return true;
   412         }
   414         // not an index property
   415         return super.defineOwnProperty(key, desc, reject);
   416     }
   418     /**
   419      * Spec. mentions use of [[DefineOwnProperty]] for indexed properties in
   420      * certain places (eg. Array.prototype.map, filter). We can not use ScriptObject.set
   421      * method in such cases. This is because set method uses inherited setters (if any)
   422      * from any object in proto chain such as Array.prototype, Object.prototype.
   423      * This method directly sets a particular element value in the current object.
   424      *
   425      * @param index key for property
   426      * @param value value to define
   427      */
   428     @Override
   429     public final void defineOwnProperty(final int index, final Object value) {
   430         assert isValidArrayIndex(index) : "invalid array index";
   431         final long longIndex = ArrayIndex.toLongIndex(index);
   432         if (longIndex >= getArray().length()) {
   433             // make array big enough to hold..
   434             setArray(getArray().ensure(longIndex));
   435         }
   436         setArray(getArray().set(index, value, false));
   437     }
   439     /**
   440      * Return the array contents upcasted as an ObjectArray, regardless of
   441      * representation
   442      *
   443      * @return an object array
   444      */
   445     public Object[] asObjectArray() {
   446         return getArray().asObjectArray();
   447     }
   449     @Override
   450     public void setIsLengthNotWritable() {
   451         super.setIsLengthNotWritable();
   452         setArray(ArrayData.setIsLengthNotWritable(getArray()));
   453     }
   455     /**
   456      * ECMA 15.4.3.2 Array.isArray ( arg )
   457      *
   458      * @param self self reference
   459      * @param arg  argument - object to check
   460      * @return true if argument is an array
   461      */
   462     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   463     public static boolean isArray(final Object self, final Object arg) {
   464         return isArray(arg) || (arg instanceof JSObject && ((JSObject)arg).isArray());
   465     }
   467     /**
   468      * Length getter
   469      * @param self self reference
   470      * @return the length of the object
   471      */
   472     @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
   473     public static Object length(final Object self) {
   474         if (isArray(self)) {
   475             final long length = ((ScriptObject) self).getArray().length();
   476             assert length >= 0L;
   477             // Cast to the narrowest supported numeric type to help optimistic type calculator
   478             if (length <= Integer.MAX_VALUE) {
   479                 return (int) length;
   480             }
   481             return (double) length;
   482         }
   484         return 0;
   485     }
   487     /**
   488      * Length setter
   489      * @param self   self reference
   490      * @param length new length property
   491      */
   492     @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
   493     public static void length(final Object self, final Object length) {
   494         if (isArray(self)) {
   495             ((ScriptObject)self).setLength(validLength(length));
   496         }
   497     }
   499     /**
   500      * Prototype length getter
   501      * @param self self reference
   502      * @return the length of the object
   503      */
   504     @Getter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
   505     public static Object getProtoLength(final Object self) {
   506         return length(self);  // Same as instance getter but we can't make nasgen use the same method for prototype
   507     }
   509     /**
   510      * Prototype length setter
   511      * @param self   self reference
   512      * @param length new length property
   513      */
   514     @Setter(name = "length", where = Where.PROTOTYPE, attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
   515     public static void setProtoLength(final Object self, final Object length) {
   516         length(self, length);  // Same as instance setter but we can't make nasgen use the same method for prototype
   517     }
   519     static long validLength(final Object length) {
   520         // ES5 15.4.5.1, steps 3.c and 3.d require two ToNumber conversions here
   521         final double doubleLength = JSType.toNumber(length);
   522         if (doubleLength != JSType.toUint32(length)) {
   523             throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length));
   524         }
   525         return (long) doubleLength;
   526     }
   528     /**
   529      * ECMA 15.4.4.2 Array.prototype.toString ( )
   530      *
   531      * @param self self reference
   532      * @return string representation of array
   533      */
   534     @Function(attributes = Attribute.NOT_ENUMERABLE)
   535     public static Object toString(final Object self) {
   536         final Object obj = Global.toObject(self);
   537         if (obj instanceof ScriptObject) {
   538             final InvokeByName joinInvoker = getJOIN();
   539             final ScriptObject sobj = (ScriptObject)obj;
   540             try {
   541                 final Object join = joinInvoker.getGetter().invokeExact(sobj);
   542                 if (Bootstrap.isCallable(join)) {
   543                     return joinInvoker.getInvoker().invokeExact(join, sobj);
   544                 }
   545             } catch (final RuntimeException | Error e) {
   546                 throw e;
   547             } catch (final Throwable t) {
   548                 throw new RuntimeException(t);
   549             }
   550         }
   552         // FIXME: should lookup Object.prototype.toString and call that?
   553         return ScriptRuntime.builtinObjectToString(self);
   554     }
   556     /**
   557      * Assert that an array is numeric, if not throw type error
   558      * @param self self array to check
   559      * @return true if numeric
   560      */
   561     @Function(attributes = Attribute.NOT_ENUMERABLE)
   562     public static Object assertNumeric(final Object self) {
   563         if(!(self instanceof NativeArray && ((NativeArray)self).getArray().getOptimisticType().isNumeric())) {
   564             throw typeError("not.a.numeric.array", ScriptRuntime.safeToString(self));
   565         }
   566         return Boolean.TRUE;
   567     }
   569     /**
   570      * ECMA 15.4.4.3 Array.prototype.toLocaleString ( )
   571      *
   572      * @param self self reference
   573      * @return locale specific string representation for array
   574      */
   575     @Function(attributes = Attribute.NOT_ENUMERABLE)
   576     public static String toLocaleString(final Object self) {
   577         final StringBuilder sb = new StringBuilder();
   578         final Iterator<Object> iter = arrayLikeIterator(self, true);
   580         while (iter.hasNext()) {
   581             final Object obj = iter.next();
   583             if (obj != null && obj != ScriptRuntime.UNDEFINED) {
   584                 final Object val = JSType.toScriptObject(obj);
   586                 try {
   587                     if (val instanceof ScriptObject) {
   588                         final InvokeByName localeInvoker = getTO_LOCALE_STRING();
   589                         final ScriptObject sobj           = (ScriptObject)val;
   590                         final Object       toLocaleString = localeInvoker.getGetter().invokeExact(sobj);
   592                         if (Bootstrap.isCallable(toLocaleString)) {
   593                             sb.append((String)localeInvoker.getInvoker().invokeExact(toLocaleString, sobj));
   594                         } else {
   595                             throw typeError("not.a.function", "toLocaleString");
   596                         }
   597                     }
   598                 } catch (final Error|RuntimeException t) {
   599                     throw t;
   600                 } catch (final Throwable t) {
   601                     throw new RuntimeException(t);
   602                 }
   603             }
   605             if (iter.hasNext()) {
   606                 sb.append(",");
   607             }
   608         }
   610         return sb.toString();
   611     }
   613     /**
   614      * ECMA 15.4.2.2 new Array (len)
   615      *
   616      * @param newObj was the new operator used to instantiate this array
   617      * @param self   self reference
   618      * @param args   arguments (length)
   619      * @return the new NativeArray
   620      */
   621     @Constructor(arity = 1)
   622     public static NativeArray construct(final boolean newObj, final Object self, final Object... args) {
   623         switch (args.length) {
   624         case 0:
   625             return new NativeArray(0);
   626         case 1:
   627             final Object len = args[0];
   628             if (len instanceof Number) {
   629                 long length;
   630                 if (len instanceof Integer || len instanceof Long) {
   631                     length = ((Number) len).longValue();
   632                     if (length >= 0 && length < JSType.MAX_UINT) {
   633                         return new NativeArray(length);
   634                     }
   635                 }
   637                 length = JSType.toUint32(len);
   639                 /*
   640                  * If the argument len is a Number and ToUint32(len) is equal to
   641                  * len, then the length property of the newly constructed object
   642                  * is set to ToUint32(len). If the argument len is a Number and
   643                  * ToUint32(len) is not equal to len, a RangeError exception is
   644                  * thrown.
   645                  */
   646                 final double numberLength = ((Number) len).doubleValue();
   647                 if (length != numberLength) {
   648                     throw rangeError("inappropriate.array.length", JSType.toString(numberLength));
   649                 }
   651                 return new NativeArray(length);
   652             }
   653             /*
   654              * If the argument len is not a Number, then the length property of
   655              * the newly constructed object is set to 1 and the 0 property of
   656              * the newly constructed object is set to len
   657              */
   658             return new NativeArray(new Object[]{args[0]});
   659             //fallthru
   660         default:
   661             return new NativeArray(args);
   662         }
   663     }
   665     /**
   666      * ECMA 15.4.2.2 new Array (len)
   667      *
   668      * Specialized constructor for zero arguments - empty array
   669      *
   670      * @param newObj was the new operator used to instantiate this array
   671      * @param self   self reference
   672      * @return the new NativeArray
   673      */
   674     @SpecializedFunction(isConstructor=true)
   675     public static NativeArray construct(final boolean newObj, final Object self) {
   676         return new NativeArray(0);
   677     }
   679     /**
   680      * ECMA 15.4.2.2 new Array (len)
   681      *
   682      * Specialized constructor for zero arguments - empty array
   683      *
   684      * @param newObj  was the new operator used to instantiate this array
   685      * @param self    self reference
   686      * @param element first element
   687      * @return the new NativeArray
   688      */
   689     @SpecializedFunction(isConstructor=true)
   690     public static Object construct(final boolean newObj, final Object self, final boolean element) {
   691         return new NativeArray(new Object[] { element });
   692     }
   694     /**
   695      * ECMA 15.4.2.2 new Array (len)
   696      *
   697      * Specialized constructor for one integer argument (length)
   698      *
   699      * @param newObj was the new operator used to instantiate this array
   700      * @param self   self reference
   701      * @param length array length
   702      * @return the new NativeArray
   703      */
   704     @SpecializedFunction(isConstructor=true)
   705     public static NativeArray construct(final boolean newObj, final Object self, final int length) {
   706         if (length >= 0) {
   707             return new NativeArray(length);
   708         }
   710         return construct(newObj, self, new Object[]{length});
   711     }
   713     /**
   714      * ECMA 15.4.2.2 new Array (len)
   715      *
   716      * Specialized constructor for one long argument (length)
   717      *
   718      * @param newObj was the new operator used to instantiate this array
   719      * @param self   self reference
   720      * @param length array length
   721      * @return the new NativeArray
   722      */
   723     @SpecializedFunction(isConstructor=true)
   724     public static NativeArray construct(final boolean newObj, final Object self, final long length) {
   725         if (length >= 0L && length <= JSType.MAX_UINT) {
   726             return new NativeArray(length);
   727         }
   729         return construct(newObj, self, new Object[]{length});
   730     }
   732     /**
   733      * ECMA 15.4.2.2 new Array (len)
   734      *
   735      * Specialized constructor for one double argument (length)
   736      *
   737      * @param newObj was the new operator used to instantiate this array
   738      * @param self   self reference
   739      * @param length array length
   740      * @return the new NativeArray
   741      */
   742     @SpecializedFunction(isConstructor=true)
   743     public static NativeArray construct(final boolean newObj, final Object self, final double length) {
   744         final long uint32length = JSType.toUint32(length);
   746         if (uint32length == length) {
   747             return new NativeArray(uint32length);
   748         }
   750         return construct(newObj, self, new Object[]{length});
   751     }
   753     /**
   754      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
   755      *
   756      * @param self self reference
   757      * @param arg argument
   758      * @return resulting NativeArray
   759      */
   760     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
   761     public static NativeArray concat(final Object self, final int arg) {
   762         final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Integer.class).copy(); //get at least an integer data copy of this data
   763         newData.fastPush(arg); //add an integer to its end
   764         return new NativeArray(newData);
   765     }
   767     /**
   768      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
   769      *
   770      * @param self self reference
   771      * @param arg argument
   772      * @return resulting NativeArray
   773      */
   774     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
   775     public static NativeArray concat(final Object self, final long arg) {
   776         final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Long.class).copy(); //get at least a long array data copy of this data
   777         newData.fastPush(arg); //add a long at the end
   778         return new NativeArray(newData);
   779     }
   781     /**
   782      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
   783      *
   784      * @param self self reference
   785      * @param arg argument
   786      * @return resulting NativeArray
   787      */
   788     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
   789     public static NativeArray concat(final Object self, final double arg) {
   790         final ContinuousArrayData newData = getContinuousArrayDataCCE(self, Double.class).copy(); //get at least a number array data copy of this data
   791         newData.fastPush(arg); //add a double at the end
   792         return new NativeArray(newData);
   793     }
   795     /**
   796      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
   797      *
   798      * @param self self reference
   799      * @param arg argument
   800      * @return resulting NativeArray
   801      */
   802     @SpecializedFunction(linkLogic=ConcatLinkLogic.class)
   803     public static NativeArray concat(final Object self, final Object arg) {
   804         //arg is [NativeArray] of same type.
   805         final ContinuousArrayData selfData = getContinuousArrayDataCCE(self);
   806         final ContinuousArrayData newData;
   808         if (arg instanceof NativeArray) {
   809             final ContinuousArrayData argData = (ContinuousArrayData)((NativeArray)arg).getArray();
   810             if (argData.isEmpty()) {
   811                 newData = selfData.copy();
   812             } else if (selfData.isEmpty()) {
   813                 newData = argData.copy();
   814             } else {
   815                 final Class<?> widestElementType = selfData.widest(argData).getBoxedElementType();
   816                 newData = ((ContinuousArrayData)selfData.convert(widestElementType)).fastConcat((ContinuousArrayData)argData.convert(widestElementType));
   817             }
   818         } else {
   819             newData = getContinuousArrayDataCCE(self, Object.class).copy();
   820             newData.fastPush(arg);
   821         }
   823         return new NativeArray(newData);
   824     }
   826     /**
   827      * ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
   828      *
   829      * @param self self reference
   830      * @param args arguments
   831      * @return resulting NativeArray
   832      */
   833     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
   834     public static NativeArray concat(final Object self, final Object... args) {
   835         final ArrayList<Object> list = new ArrayList<>();
   837         concatToList(list, Global.toObject(self));
   839         for (final Object obj : args) {
   840             concatToList(list, obj);
   841         }
   843         return new NativeArray(list.toArray());
   844     }
   846     private static void concatToList(final ArrayList<Object> list, final Object obj) {
   847         final boolean isScriptArray  = isArray(obj);
   848         final boolean isScriptObject = isScriptArray || obj instanceof ScriptObject;
   849         if (isScriptArray || obj instanceof Iterable || (obj != null && obj.getClass().isArray())) {
   850             final Iterator<Object> iter = arrayLikeIterator(obj, true);
   851             if (iter.hasNext()) {
   852                 for (int i = 0; iter.hasNext(); ++i) {
   853                     final Object value = iter.next();
   854                     final boolean lacksIndex = obj != null && !((ScriptObject)obj).has(i);
   855                     if (value == ScriptRuntime.UNDEFINED && isScriptObject && lacksIndex) {
   856                         // TODO: eventually rewrite arrayLikeIterator to use a three-state enum for handling
   857                         // UNDEFINED instead of an "includeUndefined" boolean with states SKIP, INCLUDE,
   858                         // RETURN_EMPTY. Until then, this is how we'll make sure that empty elements don't make it
   859                         // into the concatenated array.
   860                         list.add(ScriptRuntime.EMPTY);
   861                     } else {
   862                         list.add(value);
   863                     }
   864                 }
   865             } else if (!isScriptArray) {
   866                 list.add(obj); // add empty object, but not an empty array
   867             }
   868         } else {
   869             // single element, add it
   870             list.add(obj);
   871         }
   872     }
   874     /**
   875      * ECMA 15.4.4.5 Array.prototype.join (separator)
   876      *
   877      * @param self      self reference
   878      * @param separator element separator
   879      * @return string representation after join
   880      */
   881     @Function(attributes = Attribute.NOT_ENUMERABLE)
   882     public static String join(final Object self, final Object separator) {
   883         final StringBuilder    sb   = new StringBuilder();
   884         final Iterator<Object> iter = arrayLikeIterator(self, true);
   885         final String           sep  = separator == ScriptRuntime.UNDEFINED ? "," : JSType.toString(separator);
   887         while (iter.hasNext()) {
   888             final Object obj = iter.next();
   890             if (obj != null && obj != ScriptRuntime.UNDEFINED) {
   891                 sb.append(JSType.toString(obj));
   892             }
   894             if (iter.hasNext()) {
   895                 sb.append(sep);
   896             }
   897         }
   899         return sb.toString();
   900     }
   902     /**
   903      * Specialization of pop for ContinuousArrayData
   904      *   The link guard checks that the array is continuous AND not empty.
   905      *   The runtime guard checks that the guard is continuous (CCE otherwise)
   906      *
   907      * Primitive specialization, {@link LinkLogic}
   908      *
   909      * @param self self reference
   910      * @return element popped
   911      * @throws ClassCastException if array is empty, facilitating Undefined return value
   912      */
   913     @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
   914     public static int popInt(final Object self) {
   915         //must be non empty IntArrayData
   916         return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt();
   917     }
   919     /**
   920      * Specialization of pop for ContinuousArrayData
   921      *
   922      * Primitive specialization, {@link LinkLogic}
   923      *
   924      * @param self self reference
   925      * @return element popped
   926      * @throws ClassCastException if array is empty, facilitating Undefined return value
   927      */
   928     @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
   929     public static double popDouble(final Object self) {
   930         //must be non empty int long or double array data
   931         return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble();
   932     }
   934     /**
   935      * Specialization of pop for ContinuousArrayData
   936      *
   937      * Primitive specialization, {@link LinkLogic}
   938      *
   939      * @param self self reference
   940      * @return element popped
   941      * @throws ClassCastException if array is empty, facilitating Undefined return value
   942      */
   943     @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
   944     public static Object popObject(final Object self) {
   945         //can be any data, because the numeric ones will throw cce and force relink
   946         return getContinuousArrayDataCCE(self, null).fastPopObject();
   947     }
   949     /**
   950      * ECMA 15.4.4.6 Array.prototype.pop ()
   951      *
   952      * @param self self reference
   953      * @return array after pop
   954      */
   955     @Function(attributes = Attribute.NOT_ENUMERABLE)
   956     public static Object pop(final Object self) {
   957         try {
   958             final ScriptObject sobj = (ScriptObject)self;
   960             if (bulkable(sobj)) {
   961                 return sobj.getArray().pop();
   962             }
   964             final long len = JSType.toUint32(sobj.getLength());
   966             if (len == 0) {
   967                 sobj.set("length", 0, CALLSITE_STRICT);
   968                 return ScriptRuntime.UNDEFINED;
   969             }
   971             final long   index   = len - 1;
   972             final Object element = sobj.get(index);
   974             sobj.delete(index, true);
   975             sobj.set("length", index, CALLSITE_STRICT);
   977             return element;
   978         } catch (final ClassCastException | NullPointerException e) {
   979             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
   980         }
   981     }
   983     /**
   984      * ECMA 15.4.4.7 Array.prototype.push (args...)
   985      *
   986      * Primitive specialization, {@link LinkLogic}
   987      *
   988      * @param self self reference
   989      * @param arg a primitive to push
   990      * @return array length after push
   991      */
   992     @SpecializedFunction(linkLogic=PushLinkLogic.class)
   993     public static double push(final Object self, final int arg) {
   994         return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg);
   995     }
   997     /**
   998      * ECMA 15.4.4.7 Array.prototype.push (args...)
   999      *
  1000      * Primitive specialization, {@link LinkLogic}
  1002      * @param self self reference
  1003      * @param arg a primitive to push
  1004      * @return array length after push
  1005      */
  1006     @SpecializedFunction(linkLogic=PushLinkLogic.class)
  1007     public static double push(final Object self, final long arg) {
  1008         return getContinuousArrayDataCCE(self, Long.class).fastPush(arg);
  1011     /**
  1012      * ECMA 15.4.4.7 Array.prototype.push (args...)
  1014      * Primitive specialization, {@link LinkLogic}
  1016      * @param self self reference
  1017      * @param arg a primitive to push
  1018      * @return array length after push
  1019      */
  1020     @SpecializedFunction(linkLogic=PushLinkLogic.class)
  1021     public static double push(final Object self, final double arg) {
  1022         return getContinuousArrayDataCCE(self, Double.class).fastPush(arg);
  1025     /**
  1026      * ECMA 15.4.4.7 Array.prototype.push (args...)
  1028      * Primitive specialization, {@link LinkLogic}
  1030      * @param self self reference
  1031      * @param arg a primitive to push
  1032      * @return array length after push
  1033      */
  1034     @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class)
  1035     public static double pushObject(final Object self, final Object arg) {
  1036         return getContinuousArrayDataCCE(self, Object.class).fastPush(arg);
  1039     /**
  1040      * ECMA 15.4.4.7 Array.prototype.push (args...)
  1042      * @param self self reference
  1043      * @param args arguments to push
  1044      * @return array length after pushes
  1045      */
  1046     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1047     public static Object push(final Object self, final Object... args) {
  1048         try {
  1049             final ScriptObject sobj   = (ScriptObject)self;
  1051             if (bulkable(sobj) && sobj.getArray().length() + args.length <= JSType.MAX_UINT) {
  1052                 final ArrayData newData = sobj.getArray().push(true, args);
  1053                 sobj.setArray(newData);
  1054                 return JSType.toNarrowestNumber(newData.length());
  1057             long len = JSType.toUint32(sobj.getLength());
  1058             for (final Object element : args) {
  1059                 sobj.set(len++, element, CALLSITE_STRICT);
  1061             sobj.set("length", len, CALLSITE_STRICT);
  1063             return JSType.toNarrowestNumber(len);
  1064         } catch (final ClassCastException | NullPointerException e) {
  1065             throw typeError(Context.getGlobal(), e, "not.an.object", ScriptRuntime.safeToString(self));
  1069     /**
  1070      * ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument
  1072      * @param self self reference
  1073      * @param arg argument to push
  1074      * @return array after pushes
  1075      */
  1076     @SpecializedFunction
  1077     public static double push(final Object self, final Object arg) {
  1078         try {
  1079             final ScriptObject sobj = (ScriptObject)self;
  1080             final ArrayData arrayData = sobj.getArray();
  1081             final long length = arrayData.length();
  1082             if (bulkable(sobj) && length < JSType.MAX_UINT) {
  1083                 sobj.setArray(arrayData.push(true, arg));
  1084                 return length + 1;
  1087             long len = JSType.toUint32(sobj.getLength());
  1088             sobj.set(len++, arg, CALLSITE_STRICT);
  1089             sobj.set("length", len, CALLSITE_STRICT);
  1090             return len;
  1091         } catch (final ClassCastException | NullPointerException e) {
  1092             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
  1096     /**
  1097      * ECMA 15.4.4.8 Array.prototype.reverse ()
  1099      * @param self self reference
  1100      * @return reversed array
  1101      */
  1102     @Function(attributes = Attribute.NOT_ENUMERABLE)
  1103     public static Object reverse(final Object self) {
  1104         try {
  1105             final ScriptObject sobj   = (ScriptObject)self;
  1106             final long         len    = JSType.toUint32(sobj.getLength());
  1107             final long         middle = len / 2;
  1109             for (long lower = 0; lower != middle; lower++) {
  1110                 final long    upper       = len - lower - 1;
  1111                 final Object  lowerValue  = sobj.get(lower);
  1112                 final Object  upperValue  = sobj.get(upper);
  1113                 final boolean lowerExists = sobj.has(lower);
  1114                 final boolean upperExists = sobj.has(upper);
  1116                 if (lowerExists && upperExists) {
  1117                     sobj.set(lower, upperValue, CALLSITE_STRICT);
  1118                     sobj.set(upper, lowerValue, CALLSITE_STRICT);
  1119                 } else if (!lowerExists && upperExists) {
  1120                     sobj.set(lower, upperValue, CALLSITE_STRICT);
  1121                     sobj.delete(upper, true);
  1122                 } else if (lowerExists && !upperExists) {
  1123                     sobj.delete(lower, true);
  1124                     sobj.set(upper, lowerValue, CALLSITE_STRICT);
  1127             return sobj;
  1128         } catch (final ClassCastException | NullPointerException e) {
  1129             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
  1133     /**
  1134      * ECMA 15.4.4.9 Array.prototype.shift ()
  1136      * @param self self reference
  1137      * @return shifted array
  1138      */
  1139     @Function(attributes = Attribute.NOT_ENUMERABLE)
  1140     public static Object shift(final Object self) {
  1141         final Object obj = Global.toObject(self);
  1143         Object first = ScriptRuntime.UNDEFINED;
  1145         if (!(obj instanceof ScriptObject)) {
  1146             return first;
  1149         final ScriptObject sobj   = (ScriptObject) obj;
  1151         long len = JSType.toUint32(sobj.getLength());
  1153         if (len > 0) {
  1154             first = sobj.get(0);
  1156             if (bulkable(sobj)) {
  1157                 sobj.getArray().shiftLeft(1);
  1158             } else {
  1159                 boolean hasPrevious = true;
  1160                 for (long k = 1; k < len; k++) {
  1161                     final boolean hasCurrent = sobj.has(k);
  1162                     if (hasCurrent) {
  1163                         sobj.set(k - 1, sobj.get(k), CALLSITE_STRICT);
  1164                     } else if (hasPrevious) {
  1165                         sobj.delete(k - 1, true);
  1167                     hasPrevious = hasCurrent;
  1170             sobj.delete(--len, true);
  1171         } else {
  1172             len = 0;
  1175         sobj.set("length", len, CALLSITE_STRICT);
  1177         return first;
  1180     /**
  1181      * ECMA 15.4.4.10 Array.prototype.slice ( start [ , end ] )
  1183      * @param self  self reference
  1184      * @param start start of slice (inclusive)
  1185      * @param end   end of slice (optional, exclusive)
  1186      * @return sliced array
  1187      */
  1188     @Function(attributes = Attribute.NOT_ENUMERABLE)
  1189     public static Object slice(final Object self, final Object start, final Object end) {
  1190         final Object       obj                 = Global.toObject(self);
  1191         if (!(obj instanceof ScriptObject)) {
  1192             return ScriptRuntime.UNDEFINED;
  1195         final ScriptObject sobj                = (ScriptObject)obj;
  1196         final long         len                 = JSType.toUint32(sobj.getLength());
  1197         final long         relativeStart       = JSType.toLong(start);
  1198         final long         relativeEnd         = end == ScriptRuntime.UNDEFINED ? len : JSType.toLong(end);
  1200         long k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
  1201         final long finale = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
  1203         if (k >= finale) {
  1204             return new NativeArray(0);
  1207         if (bulkable(sobj)) {
  1208             return new NativeArray(sobj.getArray().slice(k, finale));
  1211         // Construct array with proper length to have a deleted filter on undefined elements
  1212         final NativeArray copy = new NativeArray(finale - k);
  1213         for (long n = 0; k < finale; n++, k++) {
  1214             if (sobj.has(k)) {
  1215                 copy.defineOwnProperty(ArrayIndex.getArrayIndex(n), sobj.get(k));
  1219         return copy;
  1222     private static Object compareFunction(final Object comparefn) {
  1223         if (comparefn == ScriptRuntime.UNDEFINED) {
  1224             return null;
  1227         if (!Bootstrap.isCallable(comparefn)) {
  1228             throw typeError("not.a.function", ScriptRuntime.safeToString(comparefn));
  1231         return comparefn;
  1234     private static Object[] sort(final Object[] array, final Object comparefn) {
  1235         final Object cmp = compareFunction(comparefn);
  1237         final List<Object> list = Arrays.asList(array);
  1238         final Object cmpThis = cmp == null || Bootstrap.isStrictCallable(cmp) ? ScriptRuntime.UNDEFINED : Global.instance();
  1240         try {
  1241             Collections.sort(list, new Comparator<Object>() {
  1242                 private final MethodHandle call_cmp = getCALL_CMP();
  1243                 @Override
  1244                 public int compare(final Object x, final Object y) {
  1245                     if (x == ScriptRuntime.UNDEFINED && y == ScriptRuntime.UNDEFINED) {
  1246                         return 0;
  1247                     } else if (x == ScriptRuntime.UNDEFINED) {
  1248                         return 1;
  1249                     } else if (y == ScriptRuntime.UNDEFINED) {
  1250                         return -1;
  1253                     if (cmp != null) {
  1254                         try {
  1255                             return (int)Math.signum((double)call_cmp.invokeExact(cmp, cmpThis, x, y));
  1256                         } catch (final RuntimeException | Error e) {
  1257                             throw e;
  1258                         } catch (final Throwable t) {
  1259                             throw new RuntimeException(t);
  1263                     return JSType.toString(x).compareTo(JSType.toString(y));
  1265             });
  1266         } catch (final IllegalArgumentException iae) {
  1267             // Collections.sort throws IllegalArgumentException when
  1268             // Comparison method violates its general contract
  1270             // See ECMA spec 15.4.4.11 Array.prototype.sort (comparefn).
  1271             // If "comparefn" is not undefined and is not a consistent
  1272             // comparison function for the elements of this array, the
  1273             // behaviour of sort is implementation-defined.
  1276         return list.toArray(new Object[array.length]);
  1279     /**
  1280      * ECMA 15.4.4.11 Array.prototype.sort ( comparefn )
  1282      * @param self       self reference
  1283      * @param comparefn  element comparison function
  1284      * @return sorted array
  1285      */
  1286     @Function(attributes = Attribute.NOT_ENUMERABLE)
  1287     public static ScriptObject sort(final Object self, final Object comparefn) {
  1288         try {
  1289             final ScriptObject sobj    = (ScriptObject) self;
  1290             final long         len     = JSType.toUint32(sobj.getLength());
  1291             ArrayData          array   = sobj.getArray();
  1293             if (len > 1) {
  1294                 // Get only non-missing elements. Missing elements go at the end
  1295                 // of the sorted array. So, just don't copy these to sort input.
  1296                 final ArrayList<Object> src = new ArrayList<>();
  1298                 for (final Iterator<Long> iter = array.indexIterator(); iter.hasNext(); ) {
  1299                     final long index = iter.next();
  1300                     if (index >= len) {
  1301                         break;
  1303                     src.add(array.getObject((int)index));
  1306                 final Object[] sorted = sort(src.toArray(), comparefn);
  1308                 for (int i = 0; i < sorted.length; i++) {
  1309                     array = array.set(i, sorted[i], true);
  1312                 // delete missing elements - which are at the end of sorted array
  1313                 if (sorted.length != len) {
  1314                     array = array.delete(sorted.length, len - 1);
  1317                 sobj.setArray(array);
  1320             return sobj;
  1321         } catch (final ClassCastException | NullPointerException e) {
  1322             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
  1326     /**
  1327      * ECMA 15.4.4.12 Array.prototype.splice ( start, deleteCount [ item1 [ , item2 [ , ... ] ] ] )
  1329      * @param self self reference
  1330      * @param args arguments
  1331      * @return result of splice
  1332      */
  1333     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 2)
  1334     public static Object splice(final Object self, final Object... args) {
  1335         final Object obj = Global.toObject(self);
  1337         if (!(obj instanceof ScriptObject)) {
  1338             return ScriptRuntime.UNDEFINED;
  1341         final Object start = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
  1342         final Object deleteCount = args.length > 1 ? args[1] : ScriptRuntime.UNDEFINED;
  1344         Object[] items;
  1346         if (args.length > 2) {
  1347             items = new Object[args.length - 2];
  1348             System.arraycopy(args, 2, items, 0, items.length);
  1349         } else {
  1350             items = ScriptRuntime.EMPTY_ARRAY;
  1353         final ScriptObject sobj                = (ScriptObject)obj;
  1354         final long         len                 = JSType.toUint32(sobj.getLength());
  1355         final long         relativeStart       = JSType.toLong(start);
  1357         final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
  1358         final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart);
  1360         NativeArray returnValue;
  1362         if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) {
  1363             try {
  1364                 returnValue =  new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length));
  1366                 // Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements
  1367                 int k = (int) actualStart;
  1368                 for (int i = 0; i < items.length; i++, k++) {
  1369                     sobj.defineOwnProperty(k, items[i]);
  1371             } catch (final UnsupportedOperationException uoe) {
  1372                 returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
  1374         } else {
  1375             returnValue = slowSplice(sobj, actualStart, actualDeleteCount, items, len);
  1378         return returnValue;
  1381     private static NativeArray slowSplice(final ScriptObject sobj, final long start, final long deleteCount, final Object[] items, final long len) {
  1383         final NativeArray array = new NativeArray(deleteCount);
  1385         for (long k = 0; k < deleteCount; k++) {
  1386             final long from = start + k;
  1388             if (sobj.has(from)) {
  1389                 array.defineOwnProperty(ArrayIndex.getArrayIndex(k), sobj.get(from));
  1393         if (items.length < deleteCount) {
  1394             for (long k = start; k < len - deleteCount; k++) {
  1395                 final long from = k + deleteCount;
  1396                 final long to   = k + items.length;
  1398                 if (sobj.has(from)) {
  1399                     sobj.set(to, sobj.get(from), CALLSITE_STRICT);
  1400                 } else {
  1401                     sobj.delete(to, true);
  1405             for (long k = len; k > len - deleteCount + items.length; k--) {
  1406                 sobj.delete(k - 1, true);
  1408         } else if (items.length > deleteCount) {
  1409             for (long k = len - deleteCount; k > start; k--) {
  1410                 final long from = k + deleteCount - 1;
  1411                 final long to   = k + items.length - 1;
  1413                 if (sobj.has(from)) {
  1414                     final Object fromValue = sobj.get(from);
  1415                     sobj.set(to, fromValue, CALLSITE_STRICT);
  1416                 } else {
  1417                     sobj.delete(to, true);
  1422         long k = start;
  1423         for (int i = 0; i < items.length; i++, k++) {
  1424             sobj.set(k, items[i], CALLSITE_STRICT);
  1427         final long newLength = len - deleteCount + items.length;
  1428         sobj.set("length", newLength, CALLSITE_STRICT);
  1430         return array;
  1433     /**
  1434      * ECMA 15.4.4.13 Array.prototype.unshift ( [ item1 [ , item2 [ , ... ] ] ] )
  1436      * @param self  self reference
  1437      * @param items items for unshift
  1438      * @return unshifted array
  1439      */
  1440     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1441     public static Object unshift(final Object self, final Object... items) {
  1442         final Object obj = Global.toObject(self);
  1444         if (!(obj instanceof ScriptObject)) {
  1445             return ScriptRuntime.UNDEFINED;
  1448         final ScriptObject sobj   = (ScriptObject)obj;
  1449         final long         len    = JSType.toUint32(sobj.getLength());
  1451         if (items == null) {
  1452             return ScriptRuntime.UNDEFINED;
  1455         if (bulkable(sobj)) {
  1456             sobj.getArray().shiftRight(items.length);
  1458             for (int j = 0; j < items.length; j++) {
  1459                 sobj.setArray(sobj.getArray().set(j, items[j], true));
  1461         } else {
  1462             for (long k = len; k > 0; k--) {
  1463                 final long from = k - 1;
  1464                 final long to = k + items.length - 1;
  1466                 if (sobj.has(from)) {
  1467                     final Object fromValue = sobj.get(from);
  1468                     sobj.set(to, fromValue, CALLSITE_STRICT);
  1469                 } else {
  1470                     sobj.delete(to, true);
  1474             for (int j = 0; j < items.length; j++) {
  1475                 sobj.set(j, items[j], CALLSITE_STRICT);
  1479         final long newLength = len + items.length;
  1480         sobj.set("length", newLength, CALLSITE_STRICT);
  1482         return JSType.toNarrowestNumber(newLength);
  1485     /**
  1486      * ECMA 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
  1488      * @param self           self reference
  1489      * @param searchElement  element to search for
  1490      * @param fromIndex      start index of search
  1491      * @return index of element, or -1 if not found
  1492      */
  1493     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1494     public static double indexOf(final Object self, final Object searchElement, final Object fromIndex) {
  1495         try {
  1496             final ScriptObject sobj = (ScriptObject)Global.toObject(self);
  1497             final long         len  = JSType.toUint32(sobj.getLength());
  1498             if (len == 0) {
  1499                 return -1;
  1502             final long         n = JSType.toLong(fromIndex);
  1503             if (n >= len) {
  1504                 return -1;
  1508             for (long k = Math.max(0, n < 0 ? len - Math.abs(n) : n); k < len; k++) {
  1509                 if (sobj.has(k)) {
  1510                     if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
  1511                         return k;
  1515         } catch (final ClassCastException | NullPointerException e) {
  1516             //fallthru
  1519         return -1;
  1522     /**
  1523      * ECMA 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
  1525      * @param self self reference
  1526      * @param args arguments: element to search for and optional from index
  1527      * @return index of element, or -1 if not found
  1528      */
  1529     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1530     public static double lastIndexOf(final Object self, final Object... args) {
  1531         try {
  1532             final ScriptObject sobj = (ScriptObject)Global.toObject(self);
  1533             final long         len  = JSType.toUint32(sobj.getLength());
  1535             if (len == 0) {
  1536                 return -1;
  1539             final Object searchElement = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
  1540             final long   n             = args.length > 1 ? JSType.toLong(args[1]) : len - 1;
  1542             for (long k = n < 0 ? len - Math.abs(n) : Math.min(n, len - 1); k >= 0; k--) {
  1543                 if (sobj.has(k)) {
  1544                     if (ScriptRuntime.EQ_STRICT(sobj.get(k), searchElement)) {
  1545                         return k;
  1549         } catch (final ClassCastException | NullPointerException e) {
  1550             throw typeError("not.an.object", ScriptRuntime.safeToString(self));
  1553         return -1;
  1556     /**
  1557      * ECMA 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] )
  1559      * @param self        self reference
  1560      * @param callbackfn  callback function per element
  1561      * @param thisArg     this argument
  1562      * @return true if callback function return true for every element in the array, false otherwise
  1563      */
  1564     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1565     public static boolean every(final Object self, final Object callbackfn, final Object thisArg) {
  1566         return applyEvery(Global.toObject(self), callbackfn, thisArg);
  1569     private static boolean applyEvery(final Object self, final Object callbackfn, final Object thisArg) {
  1570         return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, true) {
  1571             private final MethodHandle everyInvoker = getEVERY_CALLBACK_INVOKER();
  1573             @Override
  1574             protected boolean forEach(final Object val, final double i) throws Throwable {
  1575                 return result = (boolean)everyInvoker.invokeExact(callbackfn, thisArg, val, i, self);
  1577         }.apply();
  1580     /**
  1581      * ECMA 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] )
  1583      * @param self        self reference
  1584      * @param callbackfn  callback function per element
  1585      * @param thisArg     this argument
  1586      * @return true if callback function returned true for any element in the array, false otherwise
  1587      */
  1588     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1589     public static boolean some(final Object self, final Object callbackfn, final Object thisArg) {
  1590         return new IteratorAction<Boolean>(Global.toObject(self), callbackfn, thisArg, false) {
  1591             private final MethodHandle someInvoker = getSOME_CALLBACK_INVOKER();
  1593             @Override
  1594             protected boolean forEach(final Object val, final double i) throws Throwable {
  1595                 return !(result = (boolean)someInvoker.invokeExact(callbackfn, thisArg, val, i, self));
  1597         }.apply();
  1600     /**
  1601      * ECMA 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
  1603      * @param self        self reference
  1604      * @param callbackfn  callback function per element
  1605      * @param thisArg     this argument
  1606      * @return undefined
  1607      */
  1608     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1609     public static Object forEach(final Object self, final Object callbackfn, final Object thisArg) {
  1610         return new IteratorAction<Object>(Global.toObject(self), callbackfn, thisArg, ScriptRuntime.UNDEFINED) {
  1611             private final MethodHandle forEachInvoker = getFOREACH_CALLBACK_INVOKER();
  1613             @Override
  1614             protected boolean forEach(final Object val, final double i) throws Throwable {
  1615                 forEachInvoker.invokeExact(callbackfn, thisArg, val, i, self);
  1616                 return true;
  1618         }.apply();
  1621     /**
  1622      * ECMA 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
  1624      * @param self        self reference
  1625      * @param callbackfn  callback function per element
  1626      * @param thisArg     this argument
  1627      * @return array with elements transformed by map function
  1628      */
  1629     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1630     public static NativeArray map(final Object self, final Object callbackfn, final Object thisArg) {
  1631         return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, null) {
  1632             private final MethodHandle mapInvoker = getMAP_CALLBACK_INVOKER();
  1634             @Override
  1635             protected boolean forEach(final Object val, final double i) throws Throwable {
  1636                 final Object r = mapInvoker.invokeExact(callbackfn, thisArg, val, i, self);
  1637                 result.defineOwnProperty(ArrayIndex.getArrayIndex(index), r);
  1638                 return true;
  1641             @Override
  1642             public void applyLoopBegin(final ArrayLikeIterator<Object> iter0) {
  1643                 // map return array should be of same length as source array
  1644                 // even if callback reduces source array length
  1645                 result = new NativeArray(iter0.getLength());
  1647         }.apply();
  1650     /**
  1651      * ECMA 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] )
  1653      * @param self        self reference
  1654      * @param callbackfn  callback function per element
  1655      * @param thisArg     this argument
  1656      * @return filtered array
  1657      */
  1658     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1659     public static NativeArray filter(final Object self, final Object callbackfn, final Object thisArg) {
  1660         return new IteratorAction<NativeArray>(Global.toObject(self), callbackfn, thisArg, new NativeArray()) {
  1661             private long to = 0;
  1662             private final MethodHandle filterInvoker = getFILTER_CALLBACK_INVOKER();
  1664             @Override
  1665             protected boolean forEach(final Object val, final double i) throws Throwable {
  1666                 if ((boolean)filterInvoker.invokeExact(callbackfn, thisArg, val, i, self)) {
  1667                     result.defineOwnProperty(ArrayIndex.getArrayIndex(to++), val);
  1669                 return true;
  1671         }.apply();
  1674     private static Object reduceInner(final ArrayLikeIterator<Object> iter, final Object self, final Object... args) {
  1675         final Object  callbackfn          = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
  1676         final boolean initialValuePresent = args.length > 1;
  1678         Object initialValue = initialValuePresent ? args[1] : ScriptRuntime.UNDEFINED;
  1680         if (callbackfn == ScriptRuntime.UNDEFINED) {
  1681             throw typeError("not.a.function", "undefined");
  1684         if (!initialValuePresent) {
  1685             if (iter.hasNext()) {
  1686                 initialValue = iter.next();
  1687             } else {
  1688                 throw typeError("array.reduce.invalid.init");
  1692         //if initial value is ScriptRuntime.UNDEFINED - step forward once.
  1693         return new IteratorAction<Object>(Global.toObject(self), callbackfn, ScriptRuntime.UNDEFINED, initialValue, iter) {
  1694             private final MethodHandle reduceInvoker = getREDUCE_CALLBACK_INVOKER();
  1696             @Override
  1697             protected boolean forEach(final Object val, final double i) throws Throwable {
  1698                 // TODO: why can't I declare the second arg as Undefined.class?
  1699                 result = reduceInvoker.invokeExact(callbackfn, ScriptRuntime.UNDEFINED, result, val, i, self);
  1700                 return true;
  1702         }.apply();
  1705     /**
  1706      * ECMA 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
  1708      * @param self self reference
  1709      * @param args arguments to reduce
  1710      * @return accumulated result
  1711      */
  1712     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1713     public static Object reduce(final Object self, final Object... args) {
  1714         return reduceInner(arrayLikeIterator(self), self, args);
  1717     /**
  1718      * ECMA 15.4.4.22 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
  1720      * @param self        self reference
  1721      * @param args arguments to reduce
  1722      * @return accumulated result
  1723      */
  1724     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
  1725     public static Object reduceRight(final Object self, final Object... args) {
  1726         return reduceInner(reverseArrayLikeIterator(self), self, args);
  1729     /**
  1730      * Determine if Java bulk array operations may be used on the underlying
  1731      * storage. This is possible only if the object's prototype chain is empty
  1732      * or each of the prototypes in the chain is empty.
  1734      * @param self the object to examine
  1735      * @return true if optimizable
  1736      */
  1737     private static boolean bulkable(final ScriptObject self) {
  1738         return self.isArray() && !hasInheritedArrayEntries(self) && !self.isLengthNotWritable();
  1741     private static boolean hasInheritedArrayEntries(final ScriptObject self) {
  1742         ScriptObject proto = self.getProto();
  1743         while (proto != null) {
  1744             if (proto.hasArrayEntries()) {
  1745                 return true;
  1747             proto = proto.getProto();
  1750         return false;
  1753     @Override
  1754     public String toString() {
  1755         return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']';
  1758     @Override
  1759     public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
  1760         if (clazz == PushLinkLogic.class) {
  1761             return PushLinkLogic.INSTANCE;
  1762         } else if (clazz == PopLinkLogic.class) {
  1763             return PopLinkLogic.INSTANCE;
  1764         } else if (clazz == ConcatLinkLogic.class) {
  1765             return ConcatLinkLogic.INSTANCE;
  1767         return null;
  1770     @Override
  1771     public boolean hasPerInstanceAssumptions() {
  1772         return true; //length writable switchpoint
  1775     /**
  1776      * This is an abstract super class that contains common functionality for all
  1777      * specialized optimistic builtins in NativeArray. For example, it handles the
  1778      * modification switchpoint which is touched when length is written.
  1779      */
  1780     private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic {
  1781         protected ArrayLinkLogic() {
  1784         protected static ContinuousArrayData getContinuousArrayData(final Object self) {
  1785             try {
  1786                 //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop
  1787                 return (ContinuousArrayData)((NativeArray)self).getArray();
  1788             } catch (final Exception e) {
  1789                 return null;
  1793         /**
  1794          * Push and pop callsites can throw ClassCastException as a mechanism to have them
  1795          * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
  1796          * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
  1797          */
  1798         @Override
  1799         public Class<? extends Throwable> getRelinkException() {
  1800             return ClassCastException.class;
  1804     /**
  1805      * This is linker logic for optimistic concatenations
  1806      */
  1807     private static final class ConcatLinkLogic extends ArrayLinkLogic {
  1808         private static final LinkLogic INSTANCE = new ConcatLinkLogic();
  1810         @Override
  1811         public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
  1812             final Object[] args = request.getArguments();
  1814             if (args.length != 3) { //single argument check
  1815                 return false;
  1818             final ContinuousArrayData selfData = getContinuousArrayData(self);
  1819             if (selfData == null) {
  1820                 return false;
  1823             final Object arg = args[2];
  1824             //args[2] continuousarray or non arraydata, let past non array datas
  1825             if (arg instanceof NativeArray) {
  1826                 final ContinuousArrayData argData = getContinuousArrayData(arg);
  1827                 if (argData == null) {
  1828                     return false;
  1832             return true;
  1836     /**
  1837      * This is linker logic for optimistic pushes
  1838      */
  1839     private static final class PushLinkLogic extends ArrayLinkLogic {
  1840         private static final LinkLogic INSTANCE = new PushLinkLogic();
  1842         @Override
  1843         public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
  1844             return getContinuousArrayData(self) != null;
  1848     /**
  1849      * This is linker logic for optimistic pops
  1850      */
  1851     private static final class PopLinkLogic extends ArrayLinkLogic {
  1852         private static final LinkLogic INSTANCE = new PopLinkLogic();
  1854         /**
  1855          * We need to check if we are dealing with a continuous non empty array data here,
  1856          * as pop with a primitive return value returns undefined for arrays with length 0
  1857          */
  1858         @Override
  1859         public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
  1860             final ContinuousArrayData data = getContinuousNonEmptyArrayData(self);
  1861             if (data != null) {
  1862                 final Class<?> elementType = data.getElementType();
  1863                 final Class<?> returnType  = desc.getMethodType().returnType();
  1864                 final boolean  typeFits    = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType);
  1865                 return typeFits;
  1867             return false;
  1870         private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) {
  1871             final ContinuousArrayData data = getContinuousArrayData(self);
  1872             if (data != null) {
  1873                 return data.length() == 0 ? null : data;
  1875             return null;
  1879     //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic,
  1880     //so rather than synthesizing them into a guard method handle that would also perform the push on the
  1881     //retrieved receiver, we use this as runtime logic
  1883     //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin
  1884     //where everything works first
  1886     private static final <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) {
  1887         try {
  1888             @SuppressWarnings("unchecked")
  1889             final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray();
  1890             if (data.length() != 0L) {
  1891                 return data; //if length is 0 we cannot pop and have to relink, because then we'd have to return an undefined, which is a wider type than e.g. int
  1893         } catch (final NullPointerException e) {
  1894             //fallthru
  1896         throw new ClassCastException();
  1899     private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self) {
  1900         try {
  1901             return (ContinuousArrayData)((NativeArray)self).getArray();
  1902          } catch (final NullPointerException e) {
  1903              throw new ClassCastException();
  1907     private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
  1908         try {
  1909            return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
  1910         } catch (final NullPointerException e) {
  1911             throw new ClassCastException();

mercurial