# HG changeset patch # User attila # Date 1415004592 -3600 # Node ID 99571b7922c0d98145d4dca01dde5d07e79a715a # Parent 981feb6ad9ccc13a62483d7a883b67a16d344ed9 8059443: NPE when unboxing return values Reviewed-by: lagergren, sundar diff -r 981feb6ad9cc -r 99571b7922c0 src/jdk/internal/dynalink/DynamicLinkerFactory.java --- a/src/jdk/internal/dynalink/DynamicLinkerFactory.java Thu Nov 06 17:06:56 2014 +0100 +++ b/src/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() { diff -r 981feb6ad9cc -r 99571b7922c0 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 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); +} diff -r 981feb6ad9cc -r 99571b7922c0 src/jdk/internal/dynalink/support/TypeConverterFactory.java --- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java Thu Nov 06 17:06:56 2014 +0100 +++ b/src/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> 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 factories) { + public TypeConverterFactory(final Iterable 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 981feb6ad9cc -r 99571b7922c0 src/jdk/internal/dynalink/support/TypeUtilities.java --- a/src/jdk/internal/dynalink/support/TypeUtilities.java Thu Nov 06 17:06:56 2014 +0100 +++ b/src/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); + } } diff -r 981feb6ad9cc -r 99571b7922c0 src/jdk/nashorn/internal/runtime/linker/Bootstrap.java --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Thu Nov 06 17:06:56 2014 +0100 +++ b/src/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; + } } diff -r 981feb6ad9cc -r 99571b7922c0 test/script/basic/JDK-8059443.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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); diff -r 981feb6ad9cc -r 99571b7922c0 test/script/basic/JDK-8059443.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8059443.js.EXPECTED Mon Nov 03 09:49:52 2014 +0100 @@ -0,0 +1,4 @@ +yay +0 +0 +0 diff -r 981feb6ad9cc -r 99571b7922c0 test/src/jdk/nashorn/test/models/NullProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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; } +}