diff --git a/nashorn/.hgignore b/nashorn/.hgignore index 56e01bae4c0..6d68c1d476d 100644 --- a/nashorn/.hgignore +++ b/nashorn/.hgignore @@ -24,3 +24,4 @@ jcov2/* .idea/* test/lib/testng.jar test/script/external/* +.project diff --git a/nashorn/docs/JavaScriptingProgrammersGuide.html b/nashorn/docs/JavaScriptingProgrammersGuide.html index 811b29c9d51..a9803f7fd81 100644 --- a/nashorn/docs/JavaScriptingProgrammersGuide.html +++ b/nashorn/docs/JavaScriptingProgrammersGuide.html @@ -71,9 +71,20 @@ Classes Arrays
- var anArrayList = new Java.type("java.util.ArrayList")
+ var anArrayList = new (Java.type("java.util.ArrayList"))
or
@@ -496,6 +507,37 @@ However, once you retrieved the outer class, you can access the inner class as a
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 @@ print(a[0]);
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 @@ th.join();
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 @@ The use of functions can be taken even further; if you are invoking a Java metho 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 @@ var printAddInvokedArrayList = new ArrayListExtender() {
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 @@ public final class NativeJava {
*
* 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 @@ public final class NativeJava {
* 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 --git a/nashorn/src/jdk/nashorn/internal/objects/NativeMath.java b/nashorn/src/jdk/nashorn/internal/objects/NativeMath.java
index 7f330efc103..50a2d864f29 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeMath.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeMath.java
@@ -611,13 +611,11 @@ public final class NativeMath extends ScriptObject {
*/
@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 --git a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java
index 2715d4f4cde..df4aa1bff38 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java
@@ -38,6 +38,7 @@ import java.util.ArrayList;
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 @@ public final class NativeString extends ScriptObject {
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 @@ public final class NativeString extends ScriptObject {
*/
@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 @@ public final class NativeString extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static Object toUpperCase(final Object self) {
- return checkObjectToString(self).toUpperCase();
+ return checkObjectToString(self).toUpperCase(Locale.ROOT);
}
/**
diff --git a/nashorn/src/jdk/nashorn/internal/objects/NativeUint16Array.java b/nashorn/src/jdk/nashorn/internal/objects/NativeUint16Array.java
index 740bb394511..13ce347461e 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint16Array.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint16Array.java
@@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
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 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
*/
@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 --git a/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java b/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java
index 9be0131202c..fb45c2821be 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint32Array.java
@@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
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 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
*/
@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 --git a/nashorn/src/jdk/nashorn/internal/objects/NativeUint8Array.java b/nashorn/src/jdk/nashorn/internal/objects/NativeUint8Array.java
index 7d506c1d6b9..6cebcdb9c5f 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint8Array.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint8Array.java
@@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
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 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
*/
@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 --git a/nashorn/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java b/nashorn/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
index e3bacc2b9db..de171caa8a4 100644
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
@@ -28,7 +28,9 @@ package jdk.nashorn.internal.objects;
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 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData;
*/
@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 --git a/nashorn/src/jdk/nashorn/internal/parser/Parser.java b/nashorn/src/jdk/nashorn/internal/parser/Parser.java
index efb7a391586..03f65b86e11 100644
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java
@@ -1537,7 +1537,7 @@ loop:
endOfLine();
- appendStatement(new ThrowNode(throwLine, throwToken, finish, expression));
+ appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, 0));
}
/**
@@ -1597,7 +1597,7 @@ loop:
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 --git a/nashorn/src/jdk/nashorn/internal/parser/TokenType.java b/nashorn/src/jdk/nashorn/internal/parser/TokenType.java
index 657c8c46711..92f3ad745be 100644
--- a/nashorn/src/jdk/nashorn/internal/parser/TokenType.java
+++ b/nashorn/src/jdk/nashorn/internal/parser/TokenType.java
@@ -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 enum TokenType {
}
public String getNameOrType() {
- return name == null ? super.name().toLowerCase() : name;
+ return name == null ? super.name().toLowerCase(Locale.ENGLISH) : name;
}
public TokenType getNext() {
diff --git a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java
index 38effdafd55..13e9e1ce3bd 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java
@@ -75,7 +75,23 @@ public class AccessorProperty extends Property {
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 @@ public class AccessorProperty extends Property {
*/
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,18 +122,30 @@ public class AccessorProperty extends Property {
* @param delegate delegate script object to rebind receiver to
*/
public AccessorProperty(final AccessorProperty property, final ScriptObject delegate) {
- this(property);
+ super(property);
- this.getters = new MethodHandle[NOOF_TYPES];
-
- 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
@@ -267,8 +287,41 @@ public class AccessorProperty extends Property {
return new AccessorProperty(this);
}
+ @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 @@ public class AccessorProperty extends Property {
"get");
}
- return isSpill() ? MH.filterArguments(getters[i], 0, SPILLGETTER) : getters[i];
+ return getters[i];
}
private Property getWiderProperty(final Class> type) {
@@ -313,6 +366,9 @@ public class AccessorProperty extends Property {
}
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 @@ public class AccessorProperty extends Property {
mh = generateSetter(forType, type);
}
- return isSpill() ? MH.filterArguments(mh, 0, SPILLGETTER) : mh;
+ return mh;
}
@Override
@@ -363,6 +419,30 @@ public class AccessorProperty extends Property {
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 --git a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
index d36f216c2b0..afa3657c1b5 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java
@@ -35,21 +35,27 @@ import jdk.nashorn.internal.codegen.types.Type;
*/
final class CompiledFunction implements Comparable {
+ /** 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 @@ final class CompiledFunction implements Comparable {
}
MethodType type() {
- return invoker.type();
+ return type;
}
@Override
@@ -103,8 +109,8 @@ final class CompiledFunction implements Comparable {
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 @@ final class CompiledFunction implements Comparable {
* 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 --git a/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java b/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java
index dda24bbf04e..fa0dbede2db 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/DebugLogger.java
@@ -35,7 +35,6 @@ import jdk.nashorn.internal.runtime.options.Options;
*/
public final class DebugLogger {
- @SuppressWarnings("NonConstantLogger")
private final Logger logger;
private final boolean isEnabled;
diff --git a/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
index ed54b2e92a3..469245f113a 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
@@ -78,9 +78,9 @@ public final class FinalScriptFunctionData extends ScriptFunctionData {
//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 --git a/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java b/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java
index 903d1d5d524..16165fe9caa 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FindProperty.java
@@ -153,5 +153,24 @@ public final class FindProperty {
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 --git a/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java b/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
index 9101153325f..98b79150ebd 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
@@ -30,6 +30,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.util.Locale;
/**
* Utilities used by Global class.
@@ -373,10 +374,10 @@ loop:
} 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 --git a/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java b/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java
index 6106a314da2..ed4d8653425 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java
@@ -36,6 +36,8 @@ import jdk.nashorn.internal.ir.UnaryNode;
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 @@ public final class JSONFunctions {
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 @@ public final class JSONFunctions {
if (newElement == ScriptRuntime.UNDEFINED) {
valueObj.delete(key, strict);
} else {
- valueObj.set(key, newElement, strict);
+ setPropertyValue(valueObj, key, newElement, strict);
}
}
}
@@ -175,7 +177,9 @@ public final class JSONFunctions {
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 @@ public final class JSONFunctions {
}
}
+ // 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 --git a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java
index e972a99ef63..8f1f1e9e616 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java
@@ -28,6 +28,7 @@ package jdk.nashorn.internal.runtime;
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 enum JSType {
*/
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 @@ public enum JSType {
}
/**
- * 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 @@ public enum JSType {
}
/**
- * 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 --git a/nashorn/src/jdk/nashorn/internal/runtime/ListAdapter.java b/nashorn/src/jdk/nashorn/internal/runtime/ListAdapter.java
new file mode 100644
index 00000000000..cc99cd126bf
--- /dev/null
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ListAdapter.java
@@ -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
*/
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 @@ public final class NativeJavaPackage extends ScriptObject {
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 @@ public final class NativeJavaPackage extends ScriptObject {
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 --git a/nashorn/src/jdk/nashorn/internal/runtime/Property.java b/nashorn/src/jdk/nashorn/internal/runtime/Property.java
index 585fc4b3113..a5e46016a00 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/Property.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Property.java
@@ -52,6 +52,9 @@ public abstract class Property {
* 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;
@@ -351,6 +354,27 @@ public abstract class Property {
return slot;
}
+ /**
+ * 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
diff --git a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
index 03a25dab1b7..226d83d1b76 100644
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
@@ -30,6 +30,8 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
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 @@ import jdk.nashorn.internal.parser.TokenType;
*/
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