# HG changeset patch
# User lana
# Date 1415988228 28800
# Node ID fc37699ddc0ed41d4ab5da821211a6d2648c8883
# Parent 99f9e7a9cf0edf8ea17959839890d3ae90ce59c7# Parent 21bb83c7d7905ec42e4621f6918d8ebcef87197f
Merge
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/EvalFile.java
--- a/docs/source/EvalFile.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/EvalFile.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,14 +29,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class EvalFile {
- public static void main(String[] args) throws Exception {
+ public static void main(final String[] args) throws Exception {
// create a script engine manager
- ScriptEngineManager factory = new ScriptEngineManager();
+ final ScriptEngineManager factory = new ScriptEngineManager();
// create JavaScript engine
- ScriptEngine engine = factory.getEngineByName("nashorn");
+ final ScriptEngine engine = factory.getEngineByName("nashorn");
// evaluate JavaScript code from given file - specified by first argument
engine.eval(new java.io.FileReader(args[0]));
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/EvalScript.java
--- a/docs/source/EvalScript.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/EvalScript.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,14 +29,16 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class EvalScript {
- public static void main(String[] args) throws Exception {
+ public static void main(final String[] args) throws Exception {
// create a script engine manager
- ScriptEngineManager factory = new ScriptEngineManager();
+ final ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
- ScriptEngine engine = factory.getEngineByName("nashorn");
+ final ScriptEngine engine = factory.getEngineByName("nashorn");
// evaluate JavaScript code from String
engine.eval("print('Hello, World')");
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/InvokeScriptFunction.java
--- a/docs/source/InvokeScriptFunction.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/InvokeScriptFunction.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,22 +29,25 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class InvokeScriptFunction {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
- String script = "function hello(name) { print('Hello, ' + name); }";
+ final String script = "function hello(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// invoke the global function named "hello"
inv.invokeFunction("hello", "Scripting!!" );
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/InvokeScriptMethod.java
--- a/docs/source/InvokeScriptMethod.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/InvokeScriptMethod.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,26 +29,29 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class InvokeScriptMethod {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String. This code defines a script object 'obj'
// with one method called 'hello'.
- String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
+ final String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// get script object on which we want to call the method
- Object obj = engine.get("obj");
+ final Object obj = engine.get("obj");
// invoke the method named "hello" on the script object "obj"
inv.invokeMethod(obj, "hello", "Script Method !!" );
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/MultiScopes.java
--- a/docs/source/MultiScopes.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/MultiScopes.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,12 +29,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.SimpleScriptContext;
+@SuppressWarnings("javadoc")
public class MultiScopes {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
engine.put("x", "hello");
// print global variable "x"
@@ -42,9 +47,9 @@
// the above line prints "hello"
// Now, pass a different script context
- ScriptContext newContext = new SimpleScriptContext();
+ final ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
- Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
+ final Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// add new variable "x" to the new engineScope
engineScope.put("x", "world");
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/RunnableImpl.java
--- a/docs/source/RunnableImpl.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/RunnableImpl.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,28 +29,31 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class RunnableImpl {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
- String script = "function run() { print('run called'); }";
+ final String script = "function run() { print('run called'); }";
// evaluate script
engine.eval(script);
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script functions with the matching name.
- Runnable r = inv.getInterface(Runnable.class);
+ final Runnable r = inv.getInterface(Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
- Thread th = new Thread(r);
+ final Thread th = new Thread(r);
th.start();
th.join();
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/RunnableImplObject.java
--- a/docs/source/RunnableImplObject.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/RunnableImplObject.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,31 +29,34 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class RunnableImplObject {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
// JavaScript code in a String
- String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
+ final String script = "var obj = new Object(); obj.run = function() { print('run method called'); }";
// evaluate script
engine.eval(script);
// get script object on which we want to implement the interface with
- Object obj = engine.get("obj");
+ final Object obj = engine.get("obj");
- Invocable inv = (Invocable) engine;
+ final Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script methods of object 'obj'
- Runnable r = inv.getInterface(obj, Runnable.class);
+ final Runnable r = inv.getInterface(obj, Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
- Thread th = new Thread(r);
+ final Thread th = new Thread(r);
th.start();
th.join();
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e docs/source/ScriptVars.java
--- a/docs/source/ScriptVars.java Wed Nov 12 13:47:23 2014 -0800
+++ b/docs/source/ScriptVars.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,15 +29,17 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import javax.script.*;
-import java.io.*;
+import java.io.File;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+@SuppressWarnings("javadoc")
public class ScriptVars {
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("nashorn");
+ public static void main(final String[] args) throws Exception {
+ final ScriptEngineManager manager = new ScriptEngineManager();
+ final ScriptEngine engine = manager.getEngineByName("nashorn");
- File f = new File("test.txt");
+ final File f = new File("test.txt");
// expose File object as variable to script
engine.put("file", f);
diff -r 99f9e7a9cf0e -r fc37699ddc0e make/build.xml
--- a/make/build.xml Wed Nov 12 13:47:23 2014 -0800
+++ b/make/build.xml Fri Nov 14 10:03:48 2014 -0800
@@ -155,6 +155,7 @@
+ ${line.separator}
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/internal/dynalink/DynamicLinkerFactory.java
--- a/src/jdk/internal/dynalink/DynamicLinkerFactory.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/internal/dynalink/DynamicLinkerFactory.java Fri Nov 14 10:03:48 2014 -0800
@@ -97,6 +97,7 @@
import jdk.internal.dynalink.linker.GuardingDynamicLinker;
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
import jdk.internal.dynalink.support.AutoDiscovery;
import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
@@ -105,6 +106,7 @@
import jdk.internal.dynalink.support.DefaultPrelinkFilter;
import jdk.internal.dynalink.support.LinkerServicesImpl;
import jdk.internal.dynalink.support.TypeConverterFactory;
+import jdk.internal.dynalink.support.TypeUtilities;
/**
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
@@ -115,7 +117,6 @@
* @author Attila Szegedi
*/
public class DynamicLinkerFactory {
-
/**
* Default value for {@link #setUnstableRelinkThreshold(int) unstable relink threshold}.
*/
@@ -130,6 +131,7 @@
private boolean syncOnRelink = false;
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
private GuardedInvocationFilter prelinkFilter;
+ private MethodTypeConversionStrategy autoConversionStrategy;
/**
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
@@ -259,6 +261,29 @@
}
/**
+ * Sets an object representing the conversion strategy for automatic type conversions. After
+ * {@link TypeConverterFactory#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has
+ * applied all custom conversions to a method handle, it still needs to effect
+ * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
+ * can usually be automatically applied as per
+ * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
+ * However, sometimes language runtimes will want to customize even those conversions for their own call
+ * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
+ * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
+ * automatic conversion strategy, that can deal with null values. Note that when the strategy's
+ * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
+ * is invoked, the custom language conversions will already have been applied to the method handle, so by
+ * design the difference between the handle's current method type and the desired final type will always
+ * only be ones that can be subjected to method invocation conversions. The strategy also doesn't need to
+ * invoke a final {@code MethodHandle.asType()} as the converter factory will do that as the final step.
+ * @param autoConversionStrategy the strategy for applying method invocation conversions for the linker
+ * created by this factory.
+ */
+ public void setAutoConversionStrategy(final MethodTypeConversionStrategy autoConversionStrategy) {
+ this.autoConversionStrategy = autoConversionStrategy;
+ }
+
+ /**
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
* the pre-link filter.
*
@@ -324,8 +349,9 @@
prelinkFilter = new DefaultPrelinkFilter();
}
- return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
- prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
+ return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
+ autoConversionStrategy), composite), prelinkFilter, runtimeContextArgCount, syncOnRelink,
+ unstableRelinkThreshold);
}
private static ClassLoader getThreadContextClassLoader() {
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/internal/dynalink/beans/AbstractJavaLinker.java
--- a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java Fri Nov 14 10:03:48 2014 -0800
@@ -287,10 +287,21 @@
*/
private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
if(CallerSensitiveDetector.isCallerSensitive(m)) {
+ // Method has @CallerSensitive annotation
return new CallerSensitiveDynamicMethod(m);
}
+ // Method has no @CallerSensitive annotation
+ final MethodHandle mh;
+ try {
+ mh = unreflectSafely(m);
+ } catch (final IllegalAccessError e) {
+ // java.lang.invoke can in some case conservatively treat as caller sensitive methods that aren't
+ // marked with the annotation. In this case, we'll fall back to treating it as caller sensitive.
+ return new CallerSensitiveDynamicMethod(m);
+ }
+ // Proceed with non-caller sensitive
final Member member = (Member)m;
- return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName(), m instanceof Constructor);
+ return new SimpleDynamicMethod(mh, member.getDeclaringClass(), member.getName(), m instanceof Constructor);
}
/**
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/internal/dynalink/linker/MethodTypeConversionStrategy.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/internal/dynalink/linker/MethodTypeConversionStrategy.java Fri Nov 14 10:03:48 2014 -0800
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+ Copyright 2014 Attila Szegedi
+
+ Licensed under both the Apache License, Version 2.0 (the "Apache License")
+ and the BSD License (the "BSD License"), with licensee being free to
+ choose either of the two at their discretion.
+
+ You may not use this file except in compliance with either the Apache
+ License or the BSD License.
+
+ If you choose to use this file in compliance with the Apache License, the
+ following notice applies to you:
+
+ You may obtain a copy of the Apache License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+ If you choose to use this file in compliance with the BSD License, the
+ following notice applies to you:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.linker;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+/**
+ * Interface for objects representing a strategy for converting a method handle to a new type.
+ */
+public interface MethodTypeConversionStrategy {
+ /**
+ * Converts a method handle to a new type.
+ * @param target target method handle
+ * @param newType new type
+ * @return target converted to the new type.
+ */
+ public MethodHandle asType(final MethodHandle target, final MethodType newType);
+}
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/internal/dynalink/support/TypeConverterFactory.java
--- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/internal/dynalink/support/TypeConverterFactory.java Fri Nov 14 10:03:48 2014 -0800
@@ -97,6 +97,7 @@
import jdk.internal.dynalink.linker.GuardedTypeConversion;
import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
/**
* A factory for type converters. This class is the main implementation behind the
@@ -109,6 +110,7 @@
private final GuardingTypeConverterFactory[] factories;
private final ConversionComparator[] comparators;
+ private final MethodTypeConversionStrategy autoConversionStrategy;
private final ClassValue> converterMap = new ClassValue>() {
@Override
@@ -177,8 +179,24 @@
* Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances.
*
* @param factories the {@link GuardingTypeConverterFactory} instances to compose.
+ * @param autoConversionStrategy conversion strategy for automatic type conversions. After
+ * {@link #asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has applied all custom
+ * conversions to a method handle, it still needs to effect
+ * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
+ * can usually be automatically applied as per
+ * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
+ * However, sometimes language runtimes will want to customize even those conversions for their own call
+ * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
+ * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
+ * automatic conversion strategy, that can deal with null values. Note that when the strategy's
+ * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
+ * is invoked, the custom language conversions will already have been applied to the method handle, so by
+ * design the difference between the handle's current method type and the desired final type will always
+ * only be ones that can be subjected to method invocation conversions. Can be null, in which case no
+ * custom strategy is employed.
*/
- public TypeConverterFactory(final Iterable extends GuardingTypeConverterFactory> factories) {
+ public TypeConverterFactory(final Iterable extends GuardingTypeConverterFactory> factories,
+ final MethodTypeConversionStrategy autoConversionStrategy) {
final List l = new LinkedList<>();
final List c = new LinkedList<>();
for(final GuardingTypeConverterFactory factory: factories) {
@@ -189,20 +207,24 @@
}
this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]);
this.comparators = c.toArray(new ConversionComparator[c.size()]);
-
+ this.autoConversionStrategy = autoConversionStrategy;
}
/**
* Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by
* {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of
- * parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
- * wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
- * it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
- * provided by {@link GuardingTypeConverterFactory} implementations.
+ * parameters. For all conversions that are not a JLS method invocation conversion it'll insert
+ * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
+ * provided by {@link GuardingTypeConverterFactory} implementations. For the remaining JLS method invocation
+ * conversions, it will invoke {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)} first
+ * if an automatic conversion strategy was specified in the
+ * {@link #TypeConverterFactory(Iterable, MethodTypeConversionStrategy) constructor}, and finally apply
+ * {@link MethodHandle#asType(MethodType)} for any remaining conversions.
*
* @param handle target method handle
* @param fromType the types of source arguments
- * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
+ * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
+ * {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}, and
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
* {@link GuardingTypeConverterFactory} produced type converters as filters.
*/
@@ -246,8 +268,12 @@
}
}
- // Take care of automatic conversions
- return newHandle.asType(fromType);
+ // Give change to automatic conversion strategy, if one is present.
+ final MethodHandle autoConvertedHandle =
+ autoConversionStrategy != null ? autoConversionStrategy.asType(newHandle, fromType) : newHandle;
+
+ // Do a final asType for any conversions that remain.
+ return autoConvertedHandle.asType(fromType);
}
private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List converters) {
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/internal/dynalink/support/TypeUtilities.java
--- a/src/jdk/internal/dynalink/support/TypeUtilities.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/internal/dynalink/support/TypeUtilities.java Fri Nov 14 10:03:48 2014 -0800
@@ -520,4 +520,13 @@
public static Class> getWrapperType(final Class> primitiveType) {
return WRAPPER_TYPES.get(primitiveType);
}
+
+ /**
+ * Returns true if the passed type is a wrapper for a primitive type.
+ * @param type the examined type
+ * @return true if the passed type is a wrapper for a primitive type.
+ */
+ public static boolean isWrapperType(final Class> type) {
+ return PRIMITIVE_TYPES.containsKey(type);
+ }
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/AssertsEnabled.java
--- a/src/jdk/nashorn/internal/AssertsEnabled.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/AssertsEnabled.java Fri Nov 14 10:03:48 2014 -0800
@@ -27,8 +27,8 @@
/**
* Class that exposes the current state of asserts.
- *
*/
+@SuppressWarnings("all")
public final class AssertsEnabled {
private static boolean assertsEnabled = false;
static {
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/ApplySpecialization.java
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,6 +29,7 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
import java.lang.invoke.MethodType;
+import java.net.URL;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
@@ -93,6 +94,8 @@
private final Deque> explodedArguments = new ArrayDeque<>();
+ private final Deque callSiteTypes = new ArrayDeque<>();
+
private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
/**
@@ -118,86 +121,113 @@
return context.getLogger(this.getClass());
}
+ @SuppressWarnings("serial")
+ private static class TransformFailedException extends RuntimeException {
+ TransformFailedException(final FunctionNode fn, final String message) {
+ super(massageURL(fn.getSource().getURL()) + '.' + fn.getName() + " => " + message, null, false, false);
+ }
+ }
+
+ @SuppressWarnings("serial")
+ private static class AppliesFoundException extends RuntimeException {
+ AppliesFoundException() {
+ super("applies_found", null, false, false);
+ }
+ }
+
+ private static final AppliesFoundException HAS_APPLIES = new AppliesFoundException();
+
+ private boolean hasApplies(final FunctionNode functionNode) {
+ try {
+ functionNode.accept(new NodeVisitor(new LexicalContext()) {
+ @Override
+ public boolean enterFunctionNode(final FunctionNode fn) {
+ return fn == functionNode;
+ }
+
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ if (isApply(callNode)) {
+ throw HAS_APPLIES;
+ }
+ return true;
+ }
+ });
+ } catch (final AppliesFoundException e) {
+ return true;
+ }
+
+ log.fine("There are no applies in ", DebugLogger.quote(functionNode.getName()), " - nothing to do.");
+ return false; // no applies
+ }
+
/**
* Arguments may only be used as args to the apply. Everything else is disqualified
* We cannot control arguments if they escape from the method and go into an unknown
* scope, thus we are conservative and treat any access to arguments outside the
* apply call as a case of "we cannot apply the optimization".
- *
- * @return true if arguments escape
*/
- private boolean argumentsEscape(final FunctionNode functionNode) {
-
- @SuppressWarnings("serial")
- final UnsupportedOperationException uoe = new UnsupportedOperationException() {
- @Override
- public synchronized Throwable fillInStackTrace() {
- return null;
- }
- };
+ private static void checkValidTransform(final FunctionNode functionNode) {
final Set argumentsFound = new HashSet<>();
final Deque> stack = new ArrayDeque<>();
+
//ensure that arguments is only passed as arg to apply
- try {
- functionNode.accept(new NodeVisitor(new LexicalContext()) {
- private boolean isCurrentArg(final Expression expr) {
- return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
- }
+ functionNode.accept(new NodeVisitor(new LexicalContext()) {
+
+ private boolean isCurrentArg(final Expression expr) {
+ return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
+ }
- private boolean isArguments(final Expression expr) {
- if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
- argumentsFound.add(expr);
+ private boolean isArguments(final Expression expr) {
+ if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
+ argumentsFound.add(expr);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isParam(final String name) {
+ for (final IdentNode param : functionNode.getParameters()) {
+ if (param.getName().equals(name)) {
return true;
}
- return false;
}
+ return false;
+ }
- private boolean isParam(final String name) {
- for (final IdentNode param : functionNode.getParameters()) {
- if (param.getName().equals(name)) {
- return true;
- }
- }
- return false;
+ @Override
+ public Node leaveIdentNode(final IdentNode identNode) {
+ if (isParam(identNode.getName())) {
+ throw new TransformFailedException(lc.getCurrentFunction(), "parameter: " + identNode.getName());
}
-
- @Override
- public Node leaveIdentNode(final IdentNode identNode) {
- if (isParam(identNode.getName()) || isArguments(identNode) && !isCurrentArg(identNode)) {
- throw uoe; //avoid filling in stack trace
- }
- return identNode;
+ // it's OK if 'argument' occurs as the current argument of an apply
+ if (isArguments(identNode) && !isCurrentArg(identNode)) {
+ throw new TransformFailedException(lc.getCurrentFunction(), "is 'arguments': " + identNode.getName());
}
+ return identNode;
+ }
- @Override
- public boolean enterCallNode(final CallNode callNode) {
- final Set callArgs = new HashSet<>();
- if (isApply(callNode)) {
- final List argList = callNode.getArgs();
- if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
- throw new UnsupportedOperationException();
- }
- callArgs.addAll(callNode.getArgs());
+ @Override
+ public boolean enterCallNode(final CallNode callNode) {
+ final Set callArgs = new HashSet<>();
+ if (isApply(callNode)) {
+ final List argList = callNode.getArgs();
+ if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
+ throw new TransformFailedException(lc.getCurrentFunction(), "argument pattern not matched: " + argList);
}
- stack.push(callArgs);
- return true;
+ callArgs.addAll(callNode.getArgs());
}
+ stack.push(callArgs);
+ return true;
+ }
- @Override
- public Node leaveCallNode(final CallNode callNode) {
- stack.pop();
- return callNode;
- }
- });
- } catch (final UnsupportedOperationException e) {
- if (!argumentsFound.isEmpty()) {
- log.fine("'arguments' is used but escapes, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ @Override
+ public Node leaveCallNode(final CallNode callNode) {
+ stack.pop();
+ return callNode;
}
- return true; //bad
- }
-
- return false;
+ });
}
@Override
@@ -224,12 +254,14 @@
final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
- log.fine("Transformed ",
- callNode,
- " from apply to call => ",
- newCallNode,
- " in ",
- DebugLogger.quote(lc.getCurrentFunction().getName()));
+ if (log.isEnabled()) {
+ log.fine("Transformed ",
+ callNode,
+ " from apply to call => ",
+ newCallNode,
+ " in ",
+ DebugLogger.quote(lc.getCurrentFunction().getName()));
+ }
return newCallNode;
}
@@ -237,12 +269,12 @@
return callNode;
}
- private boolean pushExplodedArgs(final FunctionNode functionNode) {
+ private void pushExplodedArgs(final FunctionNode functionNode) {
int start = 0;
final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
if (actualCallSiteType == null) {
- return false;
+ throw new TransformFailedException(lc.getCurrentFunction(), "No callsite type");
}
assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
@@ -264,8 +296,8 @@
}
}
+ callSiteTypes.push(actualCallSiteType);
explodedArguments.push(newParams);
- return true;
}
@Override
@@ -288,11 +320,30 @@
return false;
}
- if (argumentsEscape(functionNode)) {
+ if (!hasApplies(functionNode)) {
return false;
}
- return pushExplodedArgs(functionNode);
+ if (log.isEnabled()) {
+ log.info("Trying to specialize apply to call in '",
+ functionNode.getName(),
+ "' params=",
+ functionNode.getParameters(),
+ " id=",
+ functionNode.getId(),
+ " source=",
+ massageURL(functionNode.getSource().getURL()));
+ }
+
+ try {
+ checkValidTransform(functionNode);
+ pushExplodedArgs(functionNode);
+ } catch (final TransformFailedException e) {
+ log.info("Failure: ", e.getMessage());
+ return false;
+ }
+
+ return true;
}
/**
@@ -300,8 +351,8 @@
* @return true if successful, false otherwise
*/
@Override
- public Node leaveFunctionNode(final FunctionNode functionNode0) {
- FunctionNode newFunctionNode = functionNode0;
+ public Node leaveFunctionNode(final FunctionNode functionNode) {
+ FunctionNode newFunctionNode = functionNode;
final String functionName = newFunctionNode.getName();
if (changed.contains(newFunctionNode.getId())) {
@@ -310,17 +361,18 @@
setParameters(lc, explodedArguments.peek());
if (log.isEnabled()) {
- log.info("Successfully specialized apply to call in '",
+ log.info("Success: ",
+ massageURL(newFunctionNode.getSource().getURL()),
+ '.',
functionName,
- " params=",
- explodedArguments.peek(),
"' id=",
newFunctionNode.getId(),
- " source=",
- newFunctionNode.getSource().getURL());
+ " params=",
+ callSiteTypes.peek());
}
}
+ callSiteTypes.pop();
explodedArguments.pop();
return newFunctionNode.setState(lc, CompilationState.BUILTINS_TRANSFORMED);
@@ -331,4 +383,15 @@
return f instanceof AccessNode && "apply".equals(((AccessNode)f).getProperty());
}
+ private static String massageURL(final URL url) {
+ if (url == null) {
+ return "";
+ }
+ final String str = url.toString();
+ final int slash = str.lastIndexOf('/');
+ if (slash == -1) {
+ return str;
+ }
+ return str.substring(slash + 1);
+ }
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/CodeGenerator.java
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Nov 14 10:03:48 2014 -0800
@@ -615,7 +615,6 @@
static final TypeBounds UNBOUNDED = new TypeBounds(Type.UNKNOWN, Type.OBJECT);
static final TypeBounds INT = exact(Type.INT);
- static final TypeBounds NUMBER = exact(Type.NUMBER);
static final TypeBounds OBJECT = exact(Type.OBJECT);
static final TypeBounds BOOLEAN = exact(Type.BOOLEAN);
@@ -3569,7 +3568,8 @@
operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
} else {
// Non-optimistic, non-FP +. Allow it to overflow.
- operandBounds = new TypeBounds(binaryNode.getWidestOperandType(), Type.OBJECT);
+ operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest),
+ Type.OBJECT);
forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest);
}
loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
@@ -3856,12 +3856,8 @@
operandBounds = numericBounds;
} else {
final boolean isOptimistic = isValid(getProgramPoint());
- if(isOptimistic) {
+ if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
- } else if(node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
- // Non-optimistic division must always take double arguments as its result must also be
- // double.
- operandBounds = TypeBounds.NUMBER;
} else {
// Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
@@ -3897,7 +3893,7 @@
private static boolean isRhsZero(final BinaryNode binaryNode) {
final Expression rhs = binaryNode.rhs();
- return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode)rhs).getValue());
+ return rhs instanceof LiteralNode && INT_ZERO.equals(((LiteralNode>)rhs).getValue());
}
private void loadBIT_XOR(final BinaryNode binaryNode) {
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java
--- a/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Fri Nov 14 10:03:48 2014 -0800
@@ -31,7 +31,6 @@
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
-
import jdk.nashorn.internal.IntDeque;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Block;
@@ -250,7 +249,7 @@
static Type getTypeForSlotDescriptor(final char typeDesc) {
// Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
// MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
- switch(typeDesc) {
+ switch (typeDesc) {
case 'I':
case 'i':
return Type.INT;
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/Compiler.java
--- a/src/jdk/nashorn/internal/codegen/Compiler.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java Fri Nov 14 10:03:48 2014 -0800
@@ -389,6 +389,7 @@
* @param continuationEntryPoints continuation entry points for restof method
* @param runtimeScope runtime scope for recompilation type lookup in {@code TypeEvaluator}
*/
+ @SuppressWarnings("unused")
public Compiler(
final Context context,
final ScriptEnvironment env,
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Fri Nov 14 10:03:48 2014 -0800
@@ -28,7 +28,6 @@
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
import static jdk.nashorn.internal.ir.Expression.isAlwaysFalse;
import static jdk.nashorn.internal.ir.Expression.isAlwaysTrue;
-
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@@ -236,12 +235,12 @@
private byte conversions;
void recordConversion(final LvarType from, final LvarType to) {
- switch(from) {
+ switch (from) {
case UNDEFINED:
return;
case INT:
case BOOLEAN:
- switch(to) {
+ switch (to) {
case LONG:
recordConversion(I2L);
return;
@@ -256,7 +255,7 @@
return;
}
case LONG:
- switch(to) {
+ switch (to) {
case DOUBLE:
recordConversion(L2D);
return;
@@ -1425,6 +1424,7 @@
* @param symbol the symbol representing the variable
* @param type the type
*/
+ @SuppressWarnings("unused")
private void setType(final Symbol symbol, final LvarType type) {
if(getLocalVariableTypeOrNull(symbol) == type) {
return;
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Fri Nov 14 10:03:48 2014 -0800
@@ -33,6 +33,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -221,11 +223,37 @@
private static void reportError(final String msg, final File file, final Exception e) {
final long now = System.currentTimeMillis();
if(now - lastReportedError > ERROR_REPORT_THRESHOLD) {
- getLogger().warning(String.format("Failed to %s %s", msg, file), e);
+ reportError(String.format("Failed to %s %s", msg, file), e);
lastReportedError = now;
}
}
+ /**
+ * Logs an error message with warning severity (reasoning being that we're reporting an error that'll disable the
+ * type info cache, but it's only logged as a warning because that doesn't prevent Nashorn from running, it just
+ * disables a performance-enhancing cache).
+ * @param msg the message to log
+ * @param e the exception that represents the error.
+ */
+ private static void reportError(final String msg, final Exception e) {
+ getLogger().warning(msg, "\n", exceptionToString(e));
+ }
+
+ /**
+ * A helper that prints an exception stack trace into a string. We have to do this as if we just pass the exception
+ * to {@link DebugLogger#warning(Object...)}, it will only log the exception message and not the stack, making
+ * problems harder to diagnose.
+ * @param e the exception
+ * @return the string representation of {@link Exception#printStackTrace()} output.
+ */
+ private static String exceptionToString(final Exception e) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw, false);
+ e.printStackTrace(pw);
+ pw.flush();
+ return sw.toString();
+ }
+
private static File createBaseCacheDir() {
if(MAX_FILES == 0 || Options.getBooleanProperty("nashorn.typeInfo.disabled")) {
return null;
@@ -233,7 +261,7 @@
try {
return createBaseCacheDirPrivileged();
} catch(final Exception e) {
- getLogger().warning("Failed to create cache dir", e);
+ reportError("Failed to create cache dir", e);
return null;
}
}
@@ -267,7 +295,7 @@
try {
return createCacheDirPrivileged(baseDir);
} catch(final Exception e) {
- getLogger().warning("Failed to create cache dir", e);
+ reportError("Failed to create cache dir", e);
return null;
}
}
@@ -280,7 +308,7 @@
try {
versionDirName = getVersionDirName();
} catch(final Exception e) {
- getLogger().warning("Failed to calculate version dir name", e);
+ reportError("Failed to calculate version dir name", e);
return null;
}
final File versionDir = new File(baseDir, versionDirName);
@@ -323,10 +351,17 @@
* per-code-version directory. Normally, this will create the SHA-1 digest of the nashorn.jar. In case the classpath
* for nashorn is local directory (e.g. during development), this will create the string "dev-" followed by the
* timestamp of the most recent .class file.
- * @return
+ *
+ * @return digest of currently running nashorn
+ * @throws Exception if digest could not be created
*/
- private static String getVersionDirName() throws Exception {
- final URL url = OptimisticTypesPersistence.class.getResource("");
+ public static String getVersionDirName() throws Exception {
+ // NOTE: getResource("") won't work if the JAR file doesn't have directory entries (and JAR files in JDK distro
+ // don't, or at least it's a bad idea counting on it). Alternatively, we could've tried
+ // getResource("OptimisticTypesPersistence.class") but behavior of getResource with regard to its willingness
+ // to hand out URLs to .class files is also unspecified. Therefore, the most robust way to obtain an URL to our
+ // package is to have a small non-class anchor file and start out from its URL.
+ final URL url = OptimisticTypesPersistence.class.getResource("anchor.properties");
final String protocol = url.getProtocol();
if (protocol.equals("jar")) {
// Normal deployment: nashorn.jar
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/anchor.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/codegen/anchor.properties Fri Nov 14 10:03:48 2014 -0800
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+
+# This file exists only so OptimisticTypesPersistence.getVersionDirName() can take its URL.
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/types/IntType.java
--- a/src/jdk/nashorn/internal/codegen/types/IntType.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/types/IntType.java Fri Nov 14 10:03:48 2014 -0800
@@ -55,6 +55,7 @@
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.runtime.JSType;
/**
* Type class: INT
@@ -230,19 +231,21 @@
@Override
public Type div(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer division in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.DIV_ZERO.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
+ }
return INT;
}
@Override
public Type rem(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer remainder in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.REM_ZERO.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
+ }
return INT;
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/types/LongType.java
--- a/src/jdk/nashorn/internal/codegen/types/LongType.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/types/LongType.java Fri Nov 14 10:03:48 2014 -0800
@@ -170,19 +170,21 @@
@Override
public Type div(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer division in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.DIV_ZERO_LONG.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ }
return LONG;
}
@Override
public Type rem(final MethodVisitor method, final int programPoint) {
- // Never perform non-optimistic integer remainder in JavaScript.
- assert programPoint != INVALID_PROGRAM_POINT;
-
- method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ if (programPoint == INVALID_PROGRAM_POINT) {
+ JSType.REM_ZERO_LONG.invoke(method);
+ } else {
+ method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
+ }
return LONG;
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/codegen/types/Type.java
--- a/src/jdk/nashorn/internal/codegen/types/Type.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java Fri Nov 14 10:03:48 2014 -0800
@@ -356,7 +356,7 @@
final int pp = input.readInt();
final int typeChar = input.readByte();
final Type type;
- switch(typeChar) {
+ switch (typeChar) {
case 'L': type = Type.OBJECT; break;
case 'D': type = Type.NUMBER; break;
case 'J': type = Type.LONG; break;
@@ -376,13 +376,13 @@
}
private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class> type) {
- final Map, jdk.internal.org.objectweb.asm.Type> cache = INTERNAL_TYPE_CACHE;
- jdk.internal.org.objectweb.asm.Type itype = cache.get(type);
+ final Map, jdk.internal.org.objectweb.asm.Type> c = INTERNAL_TYPE_CACHE;
+ jdk.internal.org.objectweb.asm.Type itype = c.get(type);
if (itype != null) {
return itype;
}
itype = jdk.internal.org.objectweb.asm.Type.getType(type);
- cache.put(type, itype);
+ c.put(type, itype);
return itype;
}
@@ -1155,6 +1155,10 @@
return type;
}
+ /**
+ * Read resolve
+ * @return resolved type
+ */
protected final Object readResolve() {
return Type.typeFor(clazz);
}
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/ir/BinaryNode.java
--- a/src/jdk/nashorn/internal/ir/BinaryNode.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/ir/BinaryNode.java Fri Nov 14 10:03:48 2014 -0800
@@ -264,6 +264,10 @@
case COMMARIGHT: {
return rhs.getType(localVariableTypes);
}
+ case AND:
+ case OR:{
+ return Type.widestReturnType(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes));
+ }
default:
if (isComparison()) {
return Type.BOOLEAN;
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/objects/ArrayBufferView.java
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java Fri Nov 14 10:03:48 2014 -0800
@@ -28,7 +28,6 @@
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -44,6 +43,9 @@
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
+/**
+ * ArrayBufferView, es6 class or TypedArray implementation
+ */
@ScriptClass("ArrayBufferView")
public abstract class ArrayBufferView extends ScriptObject {
private final NativeArrayBuffer buffer;
@@ -71,6 +73,13 @@
setArray(data);
}
+ /**
+ * Constructor
+ *
+ * @param buffer underlying NativeArrayBuffer
+ * @param byteOffset byte offset for buffer
+ * @param elementLength element length in bytes
+ */
protected ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
this(buffer, byteOffset, elementLength, Global.instance());
}
@@ -89,22 +98,42 @@
return factory().bytesPerElement;
}
+ /**
+ * Buffer getter as per spec
+ * @param self ArrayBufferView instance
+ * @return buffer
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static Object buffer(final Object self) {
return ((ArrayBufferView)self).buffer;
}
+ /**
+ * Buffer offset getter as per spec
+ * @param self ArrayBufferView instance
+ * @return buffer offset
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int byteOffset(final Object self) {
return ((ArrayBufferView)self).byteOffset;
}
+ /**
+ * Byte length getter as per spec
+ * @param self ArrayBufferView instance
+ * @return array buffer view length in bytes
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int byteLength(final Object self) {
final ArrayBufferView view = (ArrayBufferView)self;
return ((TypedArrayData>)view.getArray()).getElementLength() * view.bytesPerElement();
}
+ /**
+ * Length getter as per spec
+ * @param self ArrayBufferView instance
+ * @return length in elements
+ */
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
public static int length(final Object self) {
return ((ArrayBufferView)self).elementLength();
@@ -119,15 +148,29 @@
return ((TypedArrayData>)getArray()).getElementLength();
}
+ /**
+ * Factory class for byte ArrayBufferViews
+ */
protected static abstract class Factory {
final int bytesPerElement;
final int maxElementLength;
+ /**
+ * Constructor
+ *
+ * @param bytesPerElement number of bytes per element for this buffer
+ */
public Factory(final int bytesPerElement) {
this.bytesPerElement = bytesPerElement;
this.maxElementLength = Integer.MAX_VALUE / bytesPerElement;
}
+ /**
+ * Factory method
+ *
+ * @param elementLength number of elements
+ * @return new ArrayBufferView
+ */
public final ArrayBufferView construct(final int elementLength) {
if (elementLength > maxElementLength) {
throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength));
@@ -135,15 +178,47 @@
return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength);
}
- public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength);
+ /**
+ * Factory method
+ *
+ * @param buffer underlying buffer
+ * @param byteOffset byte offset
+ * @param elementLength number of elements
+ *
+ * @return new ArrayBufferView
+ */
+ public abstract ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength);
- public abstract TypedArrayData> createArrayData(ByteBuffer nb, int start, int end);
+ /**
+ * Factory method for array data
+ *
+ * @param nb underlying nativebuffer
+ * @param start start element
+ * @param end end element
+ *
+ * @return new array data
+ */
+ public abstract TypedArrayData> createArrayData(final ByteBuffer nb, final int start, final int end);
+ /**
+ * Get the class name for this type of buffer
+ *
+ * @return class name
+ */
public abstract String getClassName();
}
+ /**
+ * Get the factor for this kind of buffer
+ * @return Factory
+ */
protected abstract Factory factory();
+ /**
+ * Get the prototype for this ArrayBufferView
+ * @param global global instance
+ * @return prototype
+ */
protected abstract ScriptObject getPrototype(final Global global);
@Override
@@ -151,10 +226,23 @@
return factory().getClassName();
}
+ /**
+ * Check if this array contains floats
+ * @return true if float array (or double)
+ */
protected boolean isFloatArray() {
return false;
}
+ /**
+ * Inheritable constructor implementation
+ *
+ * @param newObj is this a new constructor
+ * @param args arguments
+ * @param factory factory
+ *
+ * @return new ArrayBufferView
+ */
protected static ArrayBufferView constructorImpl(final boolean newObj, final Object[] args, final Factory factory) {
final Object arg0 = args.length != 0 ? args[0] : 0;
final ArrayBufferView dest;
@@ -200,6 +288,15 @@
return dest;
}
+ /**
+ * Inheritable implementation of set, if no efficient implementation is available
+ *
+ * @param self ArrayBufferView instance
+ * @param array array
+ * @param offset0 array offset
+ *
+ * @return result of setter
+ */
protected static Object setImpl(final Object self, final Object array, final Object offset0) {
final ArrayBufferView dest = (ArrayBufferView)self;
final int length;
@@ -244,6 +341,15 @@
return (int)(length & Integer.MAX_VALUE);
}
+ /**
+ * Implementation of subarray if no efficient override exists
+ *
+ * @param self ArrayBufferView instance
+ * @param begin0 begin index
+ * @param end0 end index
+ *
+ * @return sub array
+ */
protected static ScriptObject subarrayImpl(final Object self, final Object begin0, final Object end0) {
final ArrayBufferView arrayView = (ArrayBufferView)self;
final int byteOffset = arrayView.byteOffset;
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/objects/Global.java
--- a/src/jdk/nashorn/internal/objects/Global.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/objects/Global.java Fri Nov 14 10:03:48 2014 -0800
@@ -29,6 +29,7 @@
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
@@ -41,7 +42,6 @@
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicReference;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -54,7 +54,6 @@
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
-import jdk.nashorn.internal.runtime.GlobalConstants;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
@@ -438,9 +437,6 @@
this.scontext = scontext;
}
- // global constants for this global - they can be replaced with MethodHandle.constant until invalidated
- private static AtomicReference gcsInstance = new AtomicReference<>();
-
@Override
protected Context getContext() {
return context;
@@ -470,11 +466,6 @@
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
- //we can only share one instance of Global constants between globals, or we consume way too much
- //memory - this is good enough for most programs
- while (gcsInstance.get() == null) {
- gcsInstance.compareAndSet(null, new GlobalConstants(context.getLogger(GlobalConstants.class)));
- }
}
/**
@@ -493,15 +484,6 @@
}
/**
- * Return the global constants map for fields that
- * can be accessed as MethodHandle.constant
- * @return constant map
- */
- public static GlobalConstants getConstants() {
- return gcsInstance.get();
- }
-
- /**
* Check if we have a Global instance
* @return true if one exists
*/
diff -r 99f9e7a9cf0e -r fc37699ddc0e src/jdk/nashorn/internal/objects/NativeArray.java
--- a/src/jdk/nashorn/internal/objects/NativeArray.java Wed Nov 12 13:47:23 2014 -0800
+++ b/src/jdk/nashorn/internal/objects/NativeArray.java Fri Nov 14 10:03:48 2014 -0800
@@ -35,7 +35,6 @@
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
import java.lang.invoke.MethodHandle;
-import java.lang.invoke.SwitchPoint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -93,17 +92,6 @@
private static final Object CALL_CMP = new Object();
private static final Object TO_LOCALE_STRING = new Object();
- private SwitchPoint lengthMadeNotWritableSwitchPoint;
- private PushLinkLogic pushLinkLogic;
- private PopLinkLogic popLinkLogic;
- private ConcatLinkLogic concatLinkLogic;
-
- /**
- * Index for the modification SwitchPoint that triggers when length
- * becomes not writable
- */
- private static final int LENGTH_NOT_WRITABLE_SWITCHPOINT = 0;
-
/*
* Constructors.
*/
@@ -271,12 +259,83 @@
@Override
public Object getLength() {
final long length = JSType.toUint32(getArray().length());
- if(length < Integer.MAX_VALUE) {
+ if (length < Integer.MAX_VALUE) {
return (int)length;
}
return length;
}
+ private boolean defineLength(final long oldLen, final PropertyDescriptor oldLenDesc, final PropertyDescriptor desc, final boolean reject) {
+ // Step 3a
+ if (!desc.has(VALUE)) {
+ return super.defineOwnProperty("length", desc, reject);
+ }
+
+ // Step 3b
+ final PropertyDescriptor newLenDesc = desc;
+
+ // Step 3c and 3d - get new length and convert to long
+ final long newLen = NativeArray.validLength(newLenDesc.getValue(), true);
+
+ // Step 3e
+ newLenDesc.setValue(newLen);
+
+ // Step 3f
+ // increasing array length - just need to set new length value (and attributes if any) and return
+ if (newLen >= oldLen) {
+ return super.defineOwnProperty("length", newLenDesc, reject);
+ }
+
+ // Step 3g
+ if (!oldLenDesc.isWritable()) {
+ if (reject) {
+ throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
+ }
+ return false;
+ }
+
+ // Step 3h and 3i
+ final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable();
+ if (!newWritable) {
+ newLenDesc.setWritable(true);
+ }
+
+ // Step 3j and 3k
+ final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject);
+ if (!succeeded) {
+ return false;
+ }
+
+ // Step 3l
+ // make sure that length is set till the point we can delete the old elements
+ long o = oldLen;
+ while (newLen < o) {
+ o--;
+ final boolean deleteSucceeded = delete(o, false);
+ if (!deleteSucceeded) {
+ newLenDesc.setValue(o + 1);
+ if (!newWritable) {
+ newLenDesc.setWritable(false);
+ }
+ super.defineOwnProperty("length", newLenDesc, false);
+ if (reject) {
+ throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
+ }
+ return false;
+ }
+ }
+
+ // Step 3m
+ if (!newWritable) {
+ // make 'length' property not writable
+ final ScriptObject newDesc = Global.newEmptyInstance();
+ newDesc.set(WRITABLE, false, 0);
+ return super.defineOwnProperty("length", newDesc, false);
+ }
+
+ return true;
+ }
+
/**
* ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
*/
@@ -290,82 +349,16 @@
// Step 2
// get old length and convert to long
- long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true);
+ final long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true);
// Step 3
if ("length".equals(key)) {
// check for length being made non-writable
+ final boolean result = defineLength(oldLen, oldLenDesc, desc, reject);
if (desc.has(WRITABLE) && !desc.isWritable()) {
setIsLengthNotWritable();
}
-
- // Step 3a
- if (!desc.has(VALUE)) {
- return super.defineOwnProperty("length", desc, reject);
- }
-
- // Step 3b
- final PropertyDescriptor newLenDesc = desc;
-
- // Step 3c and 3d - get new length and convert to long
- final long newLen = NativeArray.validLength(newLenDesc.getValue(), true);
-
- // Step 3e
- newLenDesc.setValue(newLen);
-
- // Step 3f
- // increasing array length - just need to set new length value (and attributes if any) and return
- if (newLen >= oldLen) {
- return super.defineOwnProperty("length", newLenDesc, reject);
- }
-
- // Step 3g
- if (!oldLenDesc.isWritable()) {
- if (reject) {
- throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
- }
- return false;
- }
-
- // Step 3h and 3i
- final boolean newWritable = !newLenDesc.has(WRITABLE) || newLenDesc.isWritable();
- if (!newWritable) {
- newLenDesc.setWritable(true);
- }
-
- // Step 3j and 3k
- final boolean succeeded = super.defineOwnProperty("length", newLenDesc, reject);
- if (!succeeded) {
- return false;
- }
-
- // Step 3l
- // make sure that length is set till the point we can delete the old elements
- while (newLen < oldLen) {
- oldLen--;
- final boolean deleteSucceeded = delete(oldLen, false);
- if (!deleteSucceeded) {
- newLenDesc.setValue(oldLen + 1);
- if (!newWritable) {
- newLenDesc.setWritable(false);
- }
- super.defineOwnProperty("length", newLenDesc, false);
- if (reject) {
- throw typeError("property.not.writable", "length", ScriptRuntime.safeToString(this));
- }
- return false;
- }
- }
-
- // Step 3m
- if (!newWritable) {
- // make 'length' property not writable
- final ScriptObject newDesc = Global.newEmptyInstance();
- newDesc.set(WRITABLE, false, 0);
- return super.defineOwnProperty("length", newDesc, false);
- }
-
- return true;
+ return result;
}
// Step 4a
@@ -441,23 +434,7 @@
@Override
public void setIsLengthNotWritable() {
super.setIsLengthNotWritable();
- /*
- * Switchpoints are created lazily. If we link any push or pop site,
- * we need to create the "length made not writable" switchpoint, if it
- * doesn't exist.
- *
- * If the switchpoint already exists, we will find it here, and invalidate
- * it, invalidating all previous callsites that use it.
- *
- * If the switchpoint doesn't exist, no push/pop has been linked so far,
- * because that would create it too. We invalidate it immediately and the
- * check link logic for all future callsites will fail immediately at link
- * time
- */
- if (lengthMadeNotWritableSwitchPoint == null) {
- lengthMadeNotWritableSwitchPoint = new SwitchPoint();
- }
- SwitchPoint.invalidateAll(new SwitchPoint[] { lengthMadeNotWritableSwitchPoint });
+ setArray(ArrayData.setIsLengthNotWritable(getArray()));
}
/**
@@ -494,7 +471,7 @@
@Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
public static void length(final Object self, final Object length) {
if (isArray(self)) {
- ((ScriptObject) self).setLength(validLength(length, true));
+ ((ScriptObject)self).setLength(validLength(length, true));
}
}
@@ -1306,10 +1283,13 @@
// Get only non-missing elements. Missing elements go at the end
// of the sorted array. So, just don't copy these to sort input.
final ArrayList