# HG changeset patch # User lana # Date 1369871995 25200 # Node ID bf497a99ac5c975ca199d8f7244b0e14f9ae9cc5 # Parent 2fd6acba737b01e705e1f7c33588c922a3787f13# Parent 1d40bf1412e0090745e3556ca13572be508be0c5 Merge diff -r 2fd6acba737b -r bf497a99ac5c nashorn/.hgignore --- a/nashorn/.hgignore Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/.hgignore Wed May 29 16:59:55 2013 -0700 @@ -24,3 +24,4 @@ .idea/* test/lib/testng.jar test/script/external/* +.project diff -r 2fd6acba737b -r bf497a99ac5c nashorn/docs/JavaScriptingProgrammersGuide.html --- a/nashorn/docs/JavaScriptingProgrammersGuide.html Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/docs/JavaScriptingProgrammersGuide.html Wed May 29 16:59:55 2013 -0700 @@ -71,9 +71,20 @@ Arrays
- var anArrayList = new Java.type("java.util.ArrayList")
+ var anArrayList = new (Java.type("java.util.ArrayList"))
or
@@ -496,6 +507,37 @@
You can access both static and non-static inner classes. If you want to create an instance of a non-static inner class, remember to pass an instance of its outer class as the first argument to the constructor.
+
+In addition to creating new instances, the type objects returned from Java.type
calls can also be used to access the
+static fields and methods of the classes:
+
+ var File = Java.type("java.io.File")
+ File.createTempFile("nashorn", ".tmp")
+
+
+Methods with names of the form isXxx()
, getXxx()
, and setXxx()
can also be used as properties, for both instances and statics.
+
+A type object returned from Java.type
is distinct from a java.lang.Class
object. You can obtain one from the other using properties class
and static
on them.
+
+ var ArrayList = Java.type("java.util.ArrayList")
+ var a = new ArrayList
+
+ // All of the following print true:
+ print("Type acts as target of instanceof: " + (a instanceof ArrayList))
+ print("Class doesn't act as target of instanceof: " + !(a instanceof a.getClass()))
+ print("Type is not same as instance's getClass(): " + (a.getClass() !== ArrayList))
+ print("Type's `class` property is same as instance getClass(): " + (a.getClass() === ArrayList.class))
+ print("Type is same as instance getClass()'s `static` property: " + (a.getClass().static === ArrayList))
+
+
+You can think of the type object as similar to the class names as used in Java source code: you use them as the
+arguments to the new
and instanceof
operators and as the namespace for the static fields
+and methods, but they are different than the runtime Class
objects returned by getClass()
calls.
+Syntactically and semantically, this separation produces code that is most similar to Java code, where a distinction
+between compile-time class expressions and runtime class objects also exists. (Also, Java can't have the equivalent of static
+property on a Class
object since compile-time class expressions are never reified as objects).
+
-Array element access or length access is -the same as in Java. Also, a script array can be used when a Java -method expects a Java array (auto conversion). So in most cases we -don't have to create Java arrays explicitly.
+Array element access or length access is the same as in Java.
// javaarray.js
@@ -577,27 +616,31 @@
It is also possible to convert between JavaScript and Java arrays.
-Given a JavaScript array and a Java type, Java.toJavaArray
returns a Java array with the same initial contents, and with the specified component type.
+Given a JavaScript array and a Java type, Java.to
returns a Java array with the same initial contents, and with the specified array type.
var anArray = [1, "13", false]
- var javaIntArray = Java.toJavaArray(anArray, "int")
+ var javaIntArray = Java.to(anArray, "int[]")
print(javaIntArray[0]) // prints 1
print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion
print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion
-Given a Java array or Collection, Java.toJavaScriptArray
returns a JavaScript array with a shallow copy of its contents. Note that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will want to use this method.i
+You can use either a string or a type object returned from Java.type()
to specify the type of the array.
+You can also omit the array type, in which case a Object[]
will be created.
+
+Given a Java array or Collection, Java.from
returns a JavaScript array with a shallow copy of its contents. Note that in most cases, you can use Java arrays and lists natively in Nashorn; in cases where for some reason you need to have an actual JavaScript native array (e.g. to work with the array comprehensions functions), you will want to use this method.
var File = Java.type("java.io.File");
var listCurDir = new File(".").listFiles();
-var jsList = Java.toJavaScriptArray(listCurDir);
+var jsList = Java.from(listCurDir);
print(jsList);
A Java interface can be implemented in JavaScript by using a Java anonymous class-like syntax:
@@ -631,8 +674,8 @@
If a Java class is abstract, you can instantiate an anonymous subclass of it using an argument list that is applicable to any of its public or protected constructors, but inserting a JavaScript object with functions properties that provide JavaScript implementations of the abstract methods. If method names are overloaded, the JavaScript function will provide implementation for all overloads. E.g.:
@@ -671,6 +714,9 @@ Here,Timer.schedule()
expects a TimerTask
as its argument, so Nashorn creates an instance of a TimerTask subclass and uses the passed function to implement its only abstract method, run(). In this usage though, you can't use non-default constructors; the type must be either an interface, or must have a protected or public no-arg constructor.
+
To extend a concrete Java class, you have to use Java.extend
function.
Java.extend
returns a type object for a subclass of the specified Java class (or implementation of the specified interface) that acts as a script-to-Java adapter for it.
@@ -695,26 +741,178 @@
printSizeInvokedArrayList.size();
printAddInvokedArrayList.add(33, 33);
+
+The reason you must use Java.extend()
with concrete classes is that with concrete classes, there can be a
+syntactic ambiguity if you just invoke their constructor. Consider this example:
+
+var t = new java.lang.Thread({ run: function() { print("Hello!") } })
+
+
+If we allowed subclassing of concrete classes with constructor syntax, Nashorn couldn't tell if you're creating a new
+Thread
and passing it a Runnable
at this point, or you are subclassing Thread
and
+passing it a new implementation for its own run()
method.
+
+Java.extend
can in fact take a list of multiple types. At most one of the types can be a class, and the rest must
+be interfaces (the class doesn't have to be the first in the list). You will get back an object that extends the class and
+implements all the interfaces. (Obviously, if you only specify interfaces and no class, the object will extend java.lang.Object
).
+
+The methods shown so far for extending Java classes and implementing interfaces – passing an implementation JavaScript object
+or function to a constructor, or using Java.extend
with new
– all produce classes that take an
+extra JavaScript object parameter in their constructors that specifies the implementation. The implementation is therefore always bound
+to the actual instance being created with new
, and not to the whole class. This has some advantages, for example in the
+memory footprint of the runtime, as Nashorn can just create a single "universal adapter" for every combination of types being implemented.
+In reality, the below code shows that different instantiations of, say, Runnable
have the same class regardless of them having
+different JavaScript implementation objects:
+
+var Runnable = java.lang.Runnable;
+var r1 = new Runnable(function() { print("I'm runnable 1!") })
+var r2 = new Runnable(function() { print("I'm runnable 2!") })
+r1.run()
+r2.run()
+print("We share the same class: " + (r1.class === r2.class))
+
++prints: +
+
+I'm runnable 1!
+I'm runnable 2!
+We share the same class: true
+
++Sometimes, however, you'll want to extend a Java class or implement an interface with implementation bound to the class, not to +its instances. Such a need arises, for example, when you need to pass the class for instantiation to an external API; prime example +of this is the JavaFX framework where you need to pass an Application class to the FX API and let it instantiate it. +
+
+Fortunately, there's a solution for that: Java.extend()
– aside from being able to take any number of type parameters
+denoting a class to extend and interfaces to implement – can also take one last argument that has to be a JavaScript object
+that serves as the implementation for the methods. In this case, Java.extend()
will create a class that has the same
+constructors as the original class had, as they don't need to take an an extra implementation object parameter. The example below
+shows how you can create class-bound implementations, and shows that in this case, the implementation classes for different invocations
+are indeed different:
+
+var RunnableImpl1 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") })
+var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") })
+var r1 = new RunnableImpl1()
+var r2 = new RunnableImpl2()
+r1.run()
+r2.run()
+print("We share the same class: " + (r1.class === r2.class))
+
++prints: +
+
+I'm runnable 1!
+I'm runnable 2!
+We share the same class: false
+
+
+As you can see, the major difference here is that we moved the implementation object into the invocation of Java.extend
+from the constructor invocations – indeed the constructor invocations now don't even need to take an extra parameter! Since
+the implementations are bound to a class, the two classes obviously can't be the same, and we indeed see that the two runnables no
+longer share the same class – every invocation of Java.extend()
with a class-specific implementation object triggers
+the creation of a new Java adapter class.
+
+Finally, the adapter classes with class-bound implementations can still take an additional constructor parameter to further
+override the behavior on a per-instance basis. Thus, you can even combine the two approaches: you can provide part of the implementation
+in a class-based JavaScript implementation object passed to Java.extend
, and part in another object passed to the constructor.
+Whatever functions are provided by the constructor-passed object will override the functions in the class-bound object.
+
+var RunnableImpl = Java.extend(java.lang.Runnable, function() { print("I'm runnable 1!") })
+var r1 = new RunnableImpl()
+var r2 = new RunnableImpl(function() { print("I'm runnable 2!") })
+r1.run()
+r2.run()
+print("We share the same class: " + (r1.class === r2.class))
+
++prints: +
+
+I'm runnable 1!
+I'm runnable 2!
+We share the same class: true
+
Java methods can be overloaded by argument types. In Java, overload resolution occurs at compile time (performed by javac). -When calling Java methods from a script, the script -interpreter/compiler needs to select the appropriate method. With -the JavaScript engine, you do not need to do anything special - the -correct Java method overload variant is selected based on the -argument types. But, sometimes you may want (or have) to explicitly -select a particular overload variant.
+When calling Java methods from Nashorn, the appropriate method will be +selected based on the argument types at invocation time. You do not need +to do anything special – the correct Java method overload variant +is selected based automatically. You still have the option of explicitly +specifying a particular overload variant. Reasons for this include +either running into a genuine ambiguity with actual argument types, or +rarely reasons of performance – if you specify the actual overload +then the engine doesn't have to perform resolution during invocation. +Individual overloads of a Java methods are exposed as special properties +with the name of the method followed with its signature in parentheses. +You can invoke them like this:
// overload.js
var out = java.lang.System.out;
// select a particular print function
-out["println(java.lang.Object)"]("hello");
+out["println(Object)"]("hello");
++Note that you normally don't even have to use qualified class names in +the signatures as long as the unqualified name of the type is sufficient +for uniquely identifying the signature. In practice this means that only +in the extremely unlikely case that two overloads only differ in +parameter types that have identical unqualified names but come from +different packages would you need to use the fully qualified name of the +class. +
++We have previously shown some of the data type mappings between Java and JavaScript. +We saw that arrays need to be explicitly converted. We have also shown that JavaScript functions +are automatically converted to SAM types when passed as parameters to Java methods. Most other +conversions work as you would expect. +
+
+Every JavaScript object is also a java.util.Map
so APIs receiving maps will receive them directly.
+
+When numbers are passed to a Java API, they will be converted to the expected target numeric type, either boxed or
+primitive, but if the target type is less specific, say Number
or Object
, you can only
+count on them being a Number
, and have to test specifically for whether it's a boxed Double
,
+Integer
, Long
, etc. – it can be any of these due to internal optimizations. Also, you
+can pass any JavaScript value to a Java API expecting either a boxed or primitive number; the JavaScript specification's
+ToNumber
conversion algorithm will be applied to the value.
+
+In a similar vein, if a Java method expects a String
or a Boolean
, the values will be
+converted using all conversions allowed by the JavaScript specification's ToString
and ToBoolean
+conversions.
+
+Finally, a word of caution about strings. Due to internal performance optimizations of string operations, JavaScript strings are
+not always necessarily of type java.lang.String
, but they will always be of type java.lang.CharSequence
.
+If you pass them to a Java method that expects a java.lang.String
parameter, then you will naturally receive a Java
+String, but if the signature of your method is more generic, i.e. it receives a java.lang.Object
parameter, you can
+end up with an object of private engine implementation class that implements CharSequence
but is not a Java String.
+
* var anArray = [1, "13", false] - * var javaIntArray = Java.toJavaArray(anArray, "int") + * var javaIntArray = Java.to(anArray, "int[]") * print(javaIntArray[0]) // prints 1 * print(javaIntArray[1]) // prints 13, as string "13" was converted to number 13 as per ECMAScript ToNumber conversion * print(javaIntArray[2]) // prints 0, as boolean false was converted to number 0 as per ECMAScript ToNumber conversion ** @param self not used - * @param objArray the JavaScript array. Can be null. - * @param objType either a {@link #type(Object, Object) type object} or a String describing the component type of - * the Java array to create. Can not be null. If undefined, Object is assumed (allowing the argument to be omitted). - * @return a Java array with the copy of JavaScript array's contents, converted to the appropriate Java component - * type. Returns null if objArray is null. + * @param obj the script object. Can be null. + * @param objType either a {@link #type(Object, Object) type object} or a String describing the type of the Java + * object to create. Can not be null. If undefined, a "default" conversion is presumed (allowing the argument to be + * omitted). + * @return a Java object whose value corresponds to the original script object's value. Specifically, for array + * target types, returns a Java array of the same type with contents converted to the array's component type. Does + * not recursively convert for multidimensional arrays. For {@link List} or {@link Deque}, returns a live wrapper + * around the object, see {@link ListAdapter} for details. Returns null if obj is null. * @throws ClassNotFoundException if the class described by objType is not found */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object toJavaArray(final Object self, final Object objArray, final Object objType) throws ClassNotFoundException { - final StaticClass componentType = - objType instanceof StaticClass ? - (StaticClass)objType : - objType == UNDEFINED ? - StaticClass.forClass(Object.class) : - type(objType); - - if (objArray == null) { + public static Object to(final Object self, final Object obj, final Object objType) throws ClassNotFoundException { + if (obj == null) { return null; } - Global.checkObject(objArray); + Global.checkObject(obj); - return ((ScriptObject)objArray).getArray().asArrayOfType(componentType.getRepresentedClass()); + final Class> targetClass; + if(objType == UNDEFINED) { + targetClass = Object[].class; + } else { + final StaticClass targetType; + if(objType instanceof StaticClass) { + targetType = (StaticClass)objType; + } else { + targetType = type(objType); + } + targetClass = targetType.getRepresentedClass(); + } + + if(targetClass.isArray()) { + return ((ScriptObject)obj).getArray().asArrayOfType(targetClass.getComponentType()); + } + + if(targetClass == List.class || targetClass == Deque.class) { + return new ListAdapter((ScriptObject)obj); + } + + throw typeError("unsupported.java.to.type", targetClass.getName()); } /** @@ -283,7 +303,7 @@ *
* var File = Java.type("java.io.File") * var listHomeDir = new File("~").listFiles() - * var jsListHome = Java.toJavaScriptArray(listHomeDir) + * var jsListHome = Java.from(listHomeDir) * var jpegModifiedDates = jsListHome * .filter(function(val) { return val.getName().endsWith(".jpg") }) * .map(function(val) { return val.lastModified() }) @@ -294,7 +314,7 @@ * null. */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object toJavaScriptArray(final Object self, final Object objArray) { + public static Object from(final Object self, final Object objArray) { if (objArray == null) { return null; } else if (objArray instanceof Collection) { diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/objects/NativeMath.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeMath.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeMath.java Wed May 29 16:59:55 2013 -0700 @@ -611,13 +611,11 @@ */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object round(final Object self, final Object x) { - if (GlobalFunctions.isNaN(self, x)) { - return Double.NaN; - } else if (!GlobalFunctions.isFinite(self, x)) { - return x; + final double d = JSType.toNumber(x); + if (Math.getExponent(d) >= 52) { + return d; } - - return Math.round(JSType.toNumber(x)); + return Math.copySign(Math.floor(d + 0.5), d); } /** diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/objects/NativeString.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Wed May 29 16:59:55 2013 -0700 @@ -38,6 +38,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -630,17 +631,24 @@ final String str = checkObjectToString(self); final String searchStr = JSType.toString(search); + final int length = str.length(); - int from; + int end; if (pos == UNDEFINED) { - from = str.length(); + end = length; } else { final double numPos = JSType.toNumber(pos); - from = !Double.isNaN(numPos) ? (int)numPos : (int)Double.POSITIVE_INFINITY; + end = Double.isNaN(numPos) ? length : (int)numPos; + if (end < 0) { + end = 0; + } else if (end > length) { + end = length; + } } - return str.lastIndexOf(searchStr, from); + + return str.lastIndexOf(searchStr, end); } /** @@ -997,7 +1005,7 @@ */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toLowerCase(final Object self) { - return checkObjectToString(self).toLowerCase(); + return checkObjectToString(self).toLowerCase(Locale.ROOT); } /** @@ -1017,7 +1025,7 @@ */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object toUpperCase(final Object self) { - return checkObjectToString(self).toUpperCase(); + return checkObjectToString(self).toUpperCase(Locale.ROOT); } /** diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/objects/NativeUint16Array.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint16Array.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint16Array.java Wed May 29 16:59:55 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -37,7 +39,12 @@ */ @ScriptClass("Uint16Array") public final class NativeUint16Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 2; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 2; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) { diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java Wed May 29 16:59:55 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -38,7 +40,12 @@ */ @ScriptClass("Uint32Array") public final class NativeUint32Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 4; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 4; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteBegin, final int length) { diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/objects/NativeUint8Array.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint8Array.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint8Array.java Wed May 29 16:59:55 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -37,7 +39,12 @@ */ @ScriptClass("Uint8Array") public final class NativeUint8Array extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 1; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 1; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) { diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java --- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java Wed May 29 16:59:55 2013 -0700 @@ -28,7 +28,9 @@ import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; +import jdk.nashorn.internal.objects.annotations.Property; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayData; @@ -38,7 +40,12 @@ */ @ScriptClass("Uint8ClampedArray") public final class NativeUint8ClampedArray extends ArrayBufferView { - private static final int BYTES_PER_ELEMENT = 1; + /** + * The size in bytes of each element in the array. + */ + @Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE, where = Where.CONSTRUCTOR) + public static final int BYTES_PER_ELEMENT = 1; + private static final Factory FACTORY = new Factory(BYTES_PER_ELEMENT) { @Override public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) { diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/parser/Parser.java --- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java Wed May 29 16:59:55 2013 -0700 @@ -1537,7 +1537,7 @@ endOfLine(); - appendStatement(new ThrowNode(throwLine, throwToken, finish, expression)); + appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, 0)); } /** @@ -1597,7 +1597,7 @@ try { // Get CATCH body. final Block catchBody = getBlock(true); - final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody); + final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, 0); appendStatement(catchNode); } finally { catchBlock = restoreBlock(catchBlock); diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/parser/TokenType.java --- a/nashorn/src/jdk/nashorn/internal/parser/TokenType.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/parser/TokenType.java Wed May 29 16:59:55 2013 -0700 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.parser; +import java.util.Locale; import static jdk.nashorn.internal.parser.TokenKind.BINARY; import static jdk.nashorn.internal.parser.TokenKind.BRACKET; import static jdk.nashorn.internal.parser.TokenKind.FUTURE; @@ -249,7 +250,7 @@ } public String getNameOrType() { - return name == null ? super.name().toLowerCase() : name; + return name == null ? super.name().toLowerCase(Locale.ENGLISH) : name; } public TokenType getNext() { diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java --- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java Wed May 29 16:59:55 2013 -0700 @@ -75,7 +75,23 @@ private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES]; private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES]; - private static final MethodHandle SPILLGETTER = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), Lookup.GET_OBJECT_TYPE); + private static final MethodHandle SPILL_ELEMENT_GETTER; + private static final MethodHandle SPILL_ELEMENT_SETTER; + + private static final int SPILL_CACHE_SIZE = 8; + private static final MethodHandle[] SPILL_ACCESSORS = new MethodHandle[SPILL_CACHE_SIZE * 2]; + + static { + for (int i = 0; i < NOOF_TYPES; i++) { + final Type type = ACCESSOR_TYPES.get(i); + ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class); + ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass()); + } + + final MethodHandle spillGetter = MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class); + SPILL_ELEMENT_GETTER = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, spillGetter); + SPILL_ELEMENT_SETTER = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, spillGetter); + } /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ private MethodHandle primitiveGetter; @@ -96,14 +112,6 @@ */ private Class> currentType; - static { - for (int i = 0; i < NOOF_TYPES; i++) { - final Type type = ACCESSOR_TYPES.get(i); - ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class); - ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass()); - } - } - /** * Delegate constructor. This is used when adding properties to the Global scope, which * is necessary for outermost levels in a script (the ScriptObject is represented by @@ -114,19 +122,31 @@ * @param delegate delegate script object to rebind receiver to */ public AccessorProperty(final AccessorProperty property, final ScriptObject delegate) { - this(property); - - this.getters = new MethodHandle[NOOF_TYPES]; + super(property); - this.primitiveGetter = bindTo(primitiveGetter, delegate); - this.primitiveSetter = bindTo(primitiveSetter, delegate); - this.objectGetter = bindTo(objectGetter, delegate); - this.objectSetter = bindTo(objectSetter, delegate); + this.primitiveGetter = bindTo(property.primitiveGetter, delegate); + this.primitiveSetter = bindTo(property.primitiveSetter, delegate); + this.objectGetter = bindTo(property.objectGetter, delegate); + this.objectSetter = bindTo(property.objectSetter, delegate); setCurrentType(property.getCurrentType()); } /** + * Constructor for spill properties. Array getters and setters will be created on demand. + * + * @param key the property key + * @param flags the property flags + * @param slot spill slot + */ + public AccessorProperty(final String key, final int flags, final int slot) { + super(key, flags, slot); + assert (flags & IS_SPILL) == IS_SPILL; + + setCurrentType(Object.class); + } + + /** * Constructor. Similar to the constructor with both primitive getters and setters, the difference * here being that only one getter and setter (setter is optional for non writable fields) is given * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes @@ -268,7 +288,40 @@ } @Override + protected void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { + if (isSpill()) { + self.spill[getSlot()] = value; + } else { + try { + getSetter(Object.class, self.getMap()).invokeExact((Object)self, value); + } catch (final Error|RuntimeException e) { + throw e; + } catch (final Throwable e) { + throw new RuntimeException(e); + } + } + } + + @Override + protected Object getObjectValue(final ScriptObject self, final ScriptObject owner) { + if (isSpill()) { + return self.spill[getSlot()]; + } + + try { + return getGetter(Object.class).invokeExact((Object)self); + } catch (final Error|RuntimeException e) { + throw e; + } catch (final Throwable e) { + throw new RuntimeException(e); + } + } + + @Override public MethodHandle getGetter(final Class> type) { + if (isSpill() && objectGetter == null) { + objectGetter = getSpillGetter(); + } final int i = getAccessorTypeIndex(type); if (getters[i] == null) { getters[i] = debug( @@ -284,7 +337,7 @@ "get"); } - return isSpill() ? MH.filterArguments(getters[i], 0, SPILLGETTER) : getters[i]; + return getters[i]; } private Property getWiderProperty(final Class> type) { @@ -313,6 +366,9 @@ } private MethodHandle generateSetter(final Class> forType, final Class> type) { + if (isSpill() && objectSetter == null) { + objectSetter = getSpillSetter(); + } MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter); mh = MH.asType(mh, ACCESSOR_SETTER_TYPES[getAccessorTypeIndex(type)]); //has to be the case for invokeexact to work in ScriptObject mh = debug(mh, currentType, type, "set"); @@ -343,7 +399,7 @@ mh = generateSetter(forType, type); } - return isSpill() ? MH.filterArguments(mh, 0, SPILLGETTER) : mh; + return mh; } @Override @@ -363,6 +419,30 @@ setCurrentType(newType); } + private MethodHandle getSpillGetter() { + final int slot = getSlot(); + MethodHandle getter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2] : null; + if (getter == null) { + getter = MH.asType(MH.insertArguments(SPILL_ELEMENT_GETTER, 1, slot), Lookup.GET_OBJECT_TYPE); + if (slot < SPILL_CACHE_SIZE) { + SPILL_ACCESSORS[slot * 2] = getter; + } + } + return getter; + } + + private MethodHandle getSpillSetter() { + final int slot = getSlot(); + MethodHandle setter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2 + 1] : null; + if (setter == null) { + setter = MH.asType(MH.insertArguments(SPILL_ELEMENT_SETTER, 1, slot), Lookup.SET_OBJECT_TYPE); + if (slot < SPILL_CACHE_SIZE) { + SPILL_ACCESSORS[slot * 2 + 1] = setter; + } + } + return setter; + } + private static void finest(final String str) { if (DEBUG_FIELDS) { LOG.finest(str); diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java --- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Wed May 29 16:59:55 2013 -0700 @@ -35,21 +35,27 @@ */ final class CompiledFunction implements Comparable*/ public final class NativeJavaPackage extends ScriptObject { + private static final MethodHandleFunctionality MH = MethodHandleFactory.getFunctionality(); + private static final MethodHandle CLASS_NOT_FOUND = findOwnMH("classNotFound", Void.TYPE, NativeJavaPackage.class); + private static final MethodHandle TYPE_GUARD = Guards.getClassGuard(NativeJavaPackage.class); + /** Full name of package (includes path.) */ private final String name; @@ -123,6 +133,30 @@ return super.getDefaultValue(hint); } + @Override + protected GuardedInvocation findNewMethod(CallSiteDescriptor desc) { + return createClassNotFoundInvocation(desc); + } + + @Override + protected GuardedInvocation findCallMethod(CallSiteDescriptor desc, LinkRequest request) { + return createClassNotFoundInvocation(desc); + } + + private static GuardedInvocation createClassNotFoundInvocation(final CallSiteDescriptor desc) { + // If NativeJavaPackage is invoked either as a constructor or as a function, throw a ClassNotFoundException as + // we can assume the user attempted to instantiate a non-existent class. + final MethodType type = desc.getMethodType(); + return new GuardedInvocation( + MH.dropArguments(CLASS_NOT_FOUND, 1, type.parameterList().subList(1, type.parameterCount())), + type.parameterType(0) == NativeJavaPackage.class ? null : TYPE_GUARD); + } + + @SuppressWarnings("unused") + private static void classNotFound(final NativeJavaPackage pkg) throws ClassNotFoundException { + throw new ClassNotFoundException(pkg.name); + } + /** * "No such property" call placeholder. * @@ -188,4 +222,7 @@ return noSuchProperty(desc, request); } + private static MethodHandle findOwnMH(final String name, final Class> rtype, final Class>... types) { + return MH.findStatic(MethodHandles.lookup(), NativeJavaPackage.class, name, MH.type(rtype, types)); + } } diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/Property.java --- a/nashorn/src/jdk/nashorn/internal/runtime/Property.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/Property.java Wed May 29 16:59:55 2013 -0700 @@ -52,6 +52,9 @@ * we can use leave flag byte initialized with (the default) zero value. */ + /** Mask for property being both writable, enumerable and configurable */ + public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000; + /** ECMA 8.6.1 - Is this property not writable? */ public static final int NOT_WRITABLE = 0b0000_0000_0001; @@ -352,6 +355,27 @@ } /** + * Set the value of this property in {@code owner}. This allows to bypass creation of the + * setter MethodHandle for spill and user accessor properties. + * + * @param self the this object + * @param owner the owner object + * @param value the new property value + * @param strict is this a strict setter? + */ + protected abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict); + + /** + * Set the Object value of this property from {@code owner}. This allows to bypass creation of the + * getter MethodHandle for spill and user accessor properties. + * + * @param self the this object + * @param owner the owner object + * @return the property value + */ + protected abstract Object getObjectValue(ScriptObject self, ScriptObject owner); + + /** * Abstract method for retrieving the setter for the property. We do not know * anything about the internal representation when we request the setter, we only * know that the setter will take the property as a parameter of the given type. diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java --- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed May 29 16:59:55 2013 -0700 @@ -30,6 +30,8 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import jdk.nashorn.internal.codegen.Compiler; @@ -49,9 +51,16 @@ */ public final class RecompilableScriptFunctionData extends ScriptFunctionData { + /** FunctionNode with the code for this ScriptFunction */ private FunctionNode functionNode; - private final PropertyMap allocatorMap; + + /** Allocator map from makeMap() */ + private final PropertyMap allocatorMap; + + /** Code installer used for all further recompilation/specialization of this ScriptFunction */ private final CodeInstaller{ + /** The method type may be more specific than the invoker, if. e.g. + * the invoker is guarded, and a guard with a generic object only + * fallback, while the target is more specific, we still need the + * more specific type for sorting */ + private final MethodType type; private final MethodHandle invoker; private MethodHandle constructor; - CompiledFunction(final MethodHandle invoker) { - this(invoker, null); + CompiledFunction(final MethodType type, final MethodHandle invoker) { + this(type, invoker, null); } - CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) { - this.invoker = invoker; - this.constructor = constructor; //isConstructor + CompiledFunction(final MethodType type, final MethodHandle invoker, final MethodHandle constructor) { + this.type = type; + this.invoker = invoker; + this.constructor = constructor; } @Override public String toString() { - return " "; + return " "; } MethodHandle getInvoker() { @@ -69,7 +75,7 @@ } MethodType type() { - return invoker.type(); + return type; } @Override @@ -103,8 +109,8 @@ return weight() > o.weight(); } - boolean moreGenericThan(final MethodType type) { - return weight() > weight(type); + boolean moreGenericThan(final MethodType mt) { + return weight() > weight(mt); } /** @@ -112,15 +118,15 @@ * It is compatible if the types are narrower than the invocation type so that * a semantically equivalent linkage can be performed. * - * @param typesc + * @param mt type to check against * @return */ - boolean typeCompatible(final MethodType type) { - final Class>[] wantedParams = type.parameterArray(); + boolean typeCompatible(final MethodType mt) { + final Class>[] wantedParams = mt.parameterArray(); final Class>[] existingParams = type().parameterArray(); //if we are not examining a varargs type, the number of parameters must be the same - if (wantedParams.length != existingParams.length && !isVarArgsType(type)) { + if (wantedParams.length != existingParams.length && !isVarArgsType(mt)) { return false; } diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java --- a/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java Wed May 29 16:59:55 2013 -0700 @@ -35,7 +35,6 @@ */ public final class DebugLogger { - @SuppressWarnings("NonConstantLogger") private final Logger logger; private final boolean isEnabled; diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java --- a/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Wed May 29 16:59:55 2013 -0700 @@ -78,9 +78,9 @@ //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor //is too conservative a check. However, isConstructor(mh) always implies isConstructor param assert isConstructor(); - code.add(new CompiledFunction(MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor + code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor } else { - code.add(new CompiledFunction(mh)); + code.add(new CompiledFunction(mh.type(), mh)); } } diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java --- a/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java Wed May 29 16:59:55 2013 -0700 @@ -153,5 +153,24 @@ return prototype.isScope(); } + /** + * Get the property value from self as object. + * + * @return the property value + */ + public Object getObjectValue() { + return property.getObjectValue(getGetterReceiver(), getOwner()); + } + + /** + * Set the property value in self. + * + * @param value the new value + * @param strict strict flag + */ + public void setObjectValue(final Object value, final boolean strict) { + property.setObjectValue(getSetterReceiver(), getOwner(), value, strict); + } + } diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java --- a/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java Wed May 29 16:59:55 2013 -0700 @@ -30,6 +30,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.util.Locale; /** * Utilities used by Global class. @@ -373,10 +374,10 @@ } else if (ch < 256) { sb.append('%'); final byte b = (byte)ch; - sb.append(Integer.toHexString(b & 0xFF).toUpperCase()); + sb.append(Integer.toHexString(b & 0xFF).toUpperCase(Locale.ENGLISH)); } else { sb.append("%u"); - sb.append(Integer.toHexString(ch & 0xFFFF).toUpperCase()); + sb.append(Integer.toHexString(ch & 0xFFFF).toUpperCase(Locale.ENGLISH)); } } diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java --- a/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java Wed May 29 16:59:55 2013 -0700 @@ -36,6 +36,8 @@ import jdk.nashorn.internal.parser.JSONParser; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.linker.Bootstrap; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow; +import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; /** * Utilities used by "JSON" object implementation. @@ -94,7 +96,7 @@ if (reviver instanceof ScriptFunction) { assert global instanceof GlobalObject; final ScriptObject root = ((GlobalObject)global).newObject(); - root.set("", unfiltered, root.isStrictContext()); + root.addOwnProperty("", Property.WRITABLE_ENUMERABLE_CONFIGURABLE, unfiltered); return walk(root, "", (ScriptFunction)reviver); } return unfiltered; @@ -115,7 +117,7 @@ if (newElement == ScriptRuntime.UNDEFINED) { valueObj.delete(key, strict); } else { - valueObj.set(key, newElement, strict); + setPropertyValue(valueObj, key, newElement, strict); } } } @@ -175,7 +177,9 @@ final PropertyNode pNode = (PropertyNode) elem; final Node valueNode = pNode.getValue(); - object.set(pNode.getKeyName(), convertNode(global, valueNode), strict); + final String name = pNode.getKeyName(); + final Object value = convertNode(global, valueNode); + setPropertyValue(object, name, value, strict); } return object; @@ -188,6 +192,21 @@ } } + // add a new property if does not exist already, or else set old property + private static void setPropertyValue(final ScriptObject sobj, final String name, final Object value, final boolean strict) { + final int index = getArrayIndexNoThrow(name); + if (isValidArrayIndex(index)) { + // array index key + sobj.defineOwnProperty(index, value); + } else if (sobj.getMap().findProperty(name) != null) { + // pre-existing non-inherited property, call set + sobj.set(name, value, strict); + } else { + // add new property + sobj.addOwnProperty(name, Property.WRITABLE_ENUMERABLE_CONFIGURABLE, value); + } + } + // does the given IR node represent a numeric array? private static boolean isNumericArray(final Node[] values) { for (final Node node : values) { diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/JSType.java --- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Wed Jul 05 18:57:05 2017 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java Wed May 29 16:59:55 2013 -0700 @@ -28,6 +28,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; +import java.util.Locale; import jdk.internal.dynalink.beans.StaticClass; import jdk.nashorn.internal.codegen.CompilerConstants.Call; import jdk.nashorn.internal.parser.Lexer; @@ -111,7 +112,7 @@ */ public final String typeName() { // For NULL, "object" has to be returned! - return ((this == NULL) ? OBJECT : this).name().toLowerCase(); + return ((this == NULL) ? OBJECT : this).name().toLowerCase(Locale.ENGLISH); } /** @@ -565,8 +566,11 @@ } /** - * JavaScript compliant Object to integer conversion - * See ECMA 9.4 ToInteger + * JavaScript compliant Object to integer conversion. See ECMA 9.4 ToInteger + * + * Note that this returns {@link java.lang.Integer#MAX_VALUE} or {@link java.lang.Integer#MIN_VALUE} + * for double values that exceed the int range, including positive and negative Infinity. It is the + * caller's responsibility to handle such values correctly.
* * @param obj an object * @return an integer @@ -576,8 +580,11 @@ } /** - * JavaScript compliant Object to long conversion - * See ECMA 9.4 ToInteger + * JavaScript compliant Object to long conversion. See ECMA 9.4 ToInteger + * + *Note that this returns {@link java.lang.Long#MAX_VALUE} or {@link java.lang.Long#MIN_VALUE} + * for double values that exceed the long range, including positive and negative Infinity. It is the + * caller's responsibility to handle such values correctly.
* * @param obj an object * @return a long diff -r 2fd6acba737b -r bf497a99ac5c nashorn/src/jdk/nashorn/internal/runtime/ListAdapter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk/nashorn/internal/runtime/ListAdapter.java Wed May 29 16:59:55 2013 -0700 @@ -0,0 +1,337 @@ +package jdk.nashorn.internal.runtime; + +import java.util.AbstractList; +import java.util.Deque; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.RandomAccess; +import jdk.nashorn.internal.runtime.linker.InvokeByName; + +/** + * An adapter that can wrap any ECMAScript Array-like object (that adheres to the array rules for the property + * {@code length} and having conforming {@code push}, {@code pop}, {@code shift}, {@code unshift}, and {@code splice} + * methods) and expose it as both a Java list and double-ended queue. While script arrays aren't necessarily efficient + * as dequeues, it's still slightly more efficient to be able to translate dequeue operations into pushes, pops, shifts, + * and unshifts, than to blindly translate all list's add/remove operations into splices. Also, it is conceivable that a + * custom script object that implements an Array-like API can have a background data representation that is optimized + * for dequeue-like access. Note that with ECMAScript arrays, {@code push} and {@pop} operate at the end of the array, + * while in Java {@code Deque} they operate on the front of the queue and as such the Java dequeue {@link #push(Object)} + * and {@link #pop()} operations will translate to {@code unshift} and {@code shift} script operations respectively, + * while {@link #addLast(Object)} and {@link #removeLast()} will translate to {@code push} and {@code pop}. + */ +public class ListAdapter extends AbstractList