--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java Mon Nov 03 07:29:46 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/DynamicLinkerFactory.java Mon Nov 03 09:49:52 2014 +0100
@@ -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() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/linker/MethodTypeConversionStrategy.java Mon Nov 03 09:49:52 2014 +0100
@@ -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);
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeConverterFactory.java Mon Nov 03 07:29:46 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeConverterFactory.java Mon Nov 03 09:49:52 2014 +0100
@@ -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<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() {
@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<GuardingTypeConverterFactory> l = new LinkedList<>();
final List<ConversionComparator> 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<MethodHandle> converters) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeUtilities.java Mon Nov 03 07:29:46 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/support/TypeUtilities.java Mon Nov 03 09:49:52 2014 +0100
@@ -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);
+ }
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Mon Nov 03 07:29:46 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java Mon Nov 03 09:49:52 2014 +0100
@@ -42,6 +42,8 @@
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
+import jdk.internal.dynalink.linker.MethodTypeConversionStrategy;
+import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
@@ -106,6 +108,12 @@
return OptimisticReturnFilters.filterOptimisticReturnValue(inv, desc).asType(linkerServices, desc.getMethodType());
}
});
+ factory.setAutoConversionStrategy(new MethodTypeConversionStrategy() {
+ @Override
+ public MethodHandle asType(final MethodHandle target, final MethodType newType) {
+ return unboxReturnType(target, newType);
+ }
+ });
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
if (relinkThreshold > -1) {
factory.setUnstableRelinkThreshold(relinkThreshold);
@@ -420,4 +428,29 @@
static GuardedInvocation asTypeSafeReturn(final GuardedInvocation inv, final LinkerServices linkerServices, final CallSiteDescriptor desc) {
return inv == null ? null : inv.asTypeSafeReturn(linkerServices, desc.getMethodType());
}
+
+ /**
+ * Adapts the return type of the method handle with {@code explicitCastArguments} when it is an unboxing
+ * conversion. This will ensure that nulls are unwrapped to false or 0.
+ * @param target the target method handle
+ * @param newType the desired new type. Note that this method does not adapt the method handle completely to the
+ * new type, it only adapts the return type; this is allowed as per
+ * {@link DynamicLinkerFactory#setAutoConversionStrategy(MethodTypeConversionStrategy)}, which is what this method
+ * is used for.
+ * @return the method handle with adapted return type, if it required an unboxing conversion.
+ */
+ private static MethodHandle unboxReturnType(final MethodHandle target, final MethodType newType) {
+ final MethodType targetType = target.type();
+ final Class<?> oldReturnType = targetType.returnType();
+ if (TypeUtilities.isWrapperType(oldReturnType)) {
+ final Class<?> newReturnType = newType.returnType();
+ if (newReturnType.isPrimitive()) {
+ // The contract of setAutoConversionStrategy is such that the difference between newType and targetType
+ // can only be JLS method invocation conversions.
+ assert TypeUtilities.isMethodInvocationConvertible(oldReturnType, newReturnType);
+ return MethodHandles.explicitCastArguments(target, targetType.changeReturnType(newReturnType));
+ }
+ }
+ return target;
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8059443.js Mon Nov 03 09:49:52 2014 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8059443: NPE when unboxing return values
+ *
+ * NOTE: this test can only pass when running with a JDK where
+ * JDK-8060483 is also fixed (9b37 or later).
+ *
+ * @test
+ * @run
+ */
+
+var NullProvider = Java.type("jdk.nashorn.test.models.NullProvider");
+
+if (!NullProvider.getBoolean()) { print("yay"); }
+print(NullProvider.getLong() * (1 << 33));
+print(NullProvider.getDouble() / 2.5);
+print(NullProvider.getInteger() << 1);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8059443.js.EXPECTED Mon Nov 03 09:49:52 2014 +0100
@@ -0,0 +1,4 @@
+yay
+0
+0
+0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/nashorn/test/models/NullProvider.java Mon Nov 03 09:49:52 2014 +0100
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package jdk.nashorn.test.models;
+
+
+public class NullProvider {
+ public static Integer getInteger() { return null; }
+ public static Long getLong() { return null; }
+ public static Double getDouble() { return null; }
+ public static Boolean getBoolean() { return null; }
+}